@@ -0,0 +1,122 @@
|
||||
# Architecture
|
||||
|
||||
StepForge is split into a **dependency-free Node.js core** and a thin
|
||||
**Electron desktop shell**. All product logic lives in `core/` and
|
||||
`exporters/` and runs (and is tested) headlessly with plain `node`. The shell
|
||||
in `app/` only provides the window, the canvas UI, screen capture, hotkeys,
|
||||
and the clipboard.
|
||||
|
||||
## Repository Layout
|
||||
|
||||
```text
|
||||
app/ Electron shell: main process, preload bridge, renderer UI
|
||||
core/ Dependency-free domain logic (schema, store, archive, search,
|
||||
placeholders, render AST, png/gif/pdf/zip primitives, locks,
|
||||
snapshots, settings)
|
||||
exporters/ One module per output format, all consuming the Render AST
|
||||
scripts/ bootstrap / verify / build / package scripts (sh + ps1)
|
||||
tests/
|
||||
run_test.sh entrypoint — runs every tests/checks/test_*.sh
|
||||
checks/ shell wrappers that invoke the node test suites
|
||||
unit/ node:test workflow suites
|
||||
fixtures/ test images and guides
|
||||
examples/ sample guide + sample exports
|
||||
assets/ app icon and packaged static assets
|
||||
build/ agent_audit.md, build_report.md, artifacts_manifest.json
|
||||
docs/ file-format and data-model documentation
|
||||
vendor/ reserved for vendored deps (reuse only; nothing fetched)
|
||||
```
|
||||
|
||||
## Data Model
|
||||
|
||||
Internal working storage is **folder-based**; sharing/backups use a
|
||||
**single-file zip archive** (`.sfgz`).
|
||||
|
||||
```text
|
||||
<data root>/ (~/.local/share/stepforge, %APPDATA%/stepforge,
|
||||
settings/ or $STEPFORGE_DATA_DIR)
|
||||
app-settings.json
|
||||
placeholders.json global placeholders
|
||||
templates/<format>/<name>.template.json
|
||||
library/
|
||||
folders.json folder tree + guide->folder mapping
|
||||
guides/<guide-id>/
|
||||
guide.json guide metadata (schema below)
|
||||
steps/<step-id>/
|
||||
step.json
|
||||
original.png never mutated after capture
|
||||
working.png crop target; annotations stay vector JSON
|
||||
history/snapshots/*.zip automated + manual backups
|
||||
index/search-index.json inverted full-text index
|
||||
temp/ previews; cleaned on close
|
||||
shared-links/ linked-guide registry
|
||||
```
|
||||
|
||||
- `guide.json` — schemaVersion, guideId, title, descriptionHtml,
|
||||
placeholders, flags (focusedViewDefault, ...), stepsOrder, favorite,
|
||||
linkedSource, exportProfiles, createdAt/updatedAt.
|
||||
- `step.json` — stepId, parentStepId, kind (`image | empty | content`),
|
||||
status (`todo | in-progress | done`), title, descriptionHtml, hidden,
|
||||
skipped, focusedView {enabled, zoom, panX, panY}, image paths + size,
|
||||
`annotations[]` (normalized scene graph, coordinates in 0..1 fractions of
|
||||
the image), textBlocks[], codeBlocks[], tableBlocks[], links[].
|
||||
|
||||
All writes are **atomic** (write to `*.tmp`, fsync, rename). Deleting a guide
|
||||
moves it to `library/trash/` first.
|
||||
|
||||
## Annotation Scene Graph
|
||||
|
||||
Annotations are stored as normalized JSON, never as an editor-library blob.
|
||||
Coordinates are fractions of the image (resolution-independent). Types:
|
||||
`rect, oval, line, arrow, text, tooltip, number, blur, highlight, magnify,
|
||||
cursor`. The same geometry is rendered by the editor canvas (HTML5 canvas)
|
||||
and by the export rasterizer (`core/raster.js`), so what you see is what
|
||||
exports.
|
||||
|
||||
## Render Pipeline
|
||||
|
||||
```text
|
||||
guide.json + step.json + settings
|
||||
│ core/renderast.js (placeholder expansion, numbering, filtering
|
||||
▼ hidden/skipped, focused-view geometry)
|
||||
Render AST ──► exporters/json.js .json + steps-<title>/ images
|
||||
──► exporters/markdown.js .md + steps-<title>/ images
|
||||
──► exporters/html-simple.js single self-contained .html
|
||||
──► exporters/html-rich.js checkboxes + floating TOC
|
||||
──► exporters/pdf.js native PDF writer (core/pdf.js)
|
||||
──► exporters/gif.js GIF89a encoder (core/gif.js)
|
||||
──► exporters/image-bundle.js annotated PNGs + metadata
|
||||
──► exporters/docx.js zip+XML (core/zip.js)
|
||||
──► exporters/pptx.js zip+XML (core/zip.js)
|
||||
```
|
||||
|
||||
Image-bearing exporters rasterize annotations with `core/raster.js` on top of
|
||||
PNG pixels decoded by `core/png.js`. Every exporter accepts a template object
|
||||
(per-format settings persisted under `settings/templates/`, shareable as
|
||||
`.sfglt` zip files).
|
||||
|
||||
## Shell / Core Boundary
|
||||
|
||||
The renderer never touches the filesystem. `app/preload.js` exposes a typed
|
||||
IPC API (`stepforge.*`), and `app/main.js` routes calls into `core/`. Screen
|
||||
capture uses Electron's `desktopCapturer` (full screen, window) and an
|
||||
overlay window for region selection; hotkeys use `globalShortcut`.
|
||||
|
||||
## Security Rules
|
||||
|
||||
- Zero network code paths: no sockets, no telemetry, no update or license
|
||||
checks, no remote fonts in exports.
|
||||
- Archive imports validate every entry name against path traversal and
|
||||
absolute paths before extraction (`core/zip.js`).
|
||||
- Linked guides use sidecar `*.lock-sfgz` lock files; conflicts surface a
|
||||
keep-editing / discard dialog and last-write-wins is documented.
|
||||
- Renderer runs with `contextIsolation: true`, `nodeIntegration: false`,
|
||||
and `sandbox` enabled; only the preload API is exposed.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Change core/exporter logic together with its workflow tests in
|
||||
`tests/unit/`.
|
||||
2. Put shell checks in `tests/checks/` so the shared runner picks them up.
|
||||
3. Run `bash tests/run_test.sh` locally.
|
||||
4. Open a pull request so CI can verify on PR open.
|
||||
@@ -0,0 +1,93 @@
|
||||
# Changelog
|
||||
|
||||
All notable user-visible changes are recorded here. The format follows
|
||||
Keep-a-Changelog conventions; versions follow semver.
|
||||
|
||||
## [0.1.0] - 2026-06-10
|
||||
|
||||
Initial release.
|
||||
|
||||
### Added
|
||||
|
||||
- Welcome screen on launch: app title with three actions — New Capture
|
||||
(creates a guide, opens the editor, and starts a capture session),
|
||||
Existing Workspace (guide library), and Settings. The brand button
|
||||
returns to the welcome screen from any view.
|
||||
- Capture menu in the editor topbar: full screen / window / region /
|
||||
3-second delay, paste image as step, import images, and capture
|
||||
session start/finish — capture no longer requires the global hotkey.
|
||||
- Continuous capture sessions: steps are grabbed on every OS click where
|
||||
the platform supports it (xinput on X11, PowerShell on Windows), with
|
||||
interval auto-capture (3/5/10 s) as the always-works fallback when
|
||||
click detection or global hotkeys are unavailable (e.g. WSLg/Wayland).
|
||||
The REC bar shows the live count and trigger, with Shoot / Auto /
|
||||
Pause / Finish controls.
|
||||
- Recording sessions tuck the window away once and control everything
|
||||
from a red tray icon (capture now / pause / open / finish) instead of
|
||||
hiding the window for every shot — the app stays reachable
|
||||
mid-session, opening it auto-pauses capture, and per-shot latency
|
||||
drops because the hide-repaint wait is gone. Automatic captures also
|
||||
stand down whenever the cursor is over a visible StepForge window.
|
||||
- New captures and newly added steps are now selected in the editor.
|
||||
- The app hides its own window during capture so screenshots show your
|
||||
work, not StepForge; hotkey captures restore the window without
|
||||
stealing focus.
|
||||
- Blocks panel: add and edit informational text blocks, code blocks,
|
||||
and tables directly on a step.
|
||||
- Focused-view zoom and pan sliders.
|
||||
- Guide-level placeholders editor (More ▾ → Guide placeholders).
|
||||
- Backups & snapshots dialog with one-click undoable restore.
|
||||
- Export dialog: editable per-format options, save-as-template, and a
|
||||
template manager (rename / duplicate / delete / share as .sfglt);
|
||||
Preview now opens the generated file in the default viewer.
|
||||
- Apply an annotation's style to all annotations of the same type in
|
||||
the step or the whole guide.
|
||||
- Keyboard shortcuts: tool keys (S R O L A T G N B H M U C), PageUp/
|
||||
PageDown step navigation, Ctrl+= / Ctrl+- / Ctrl+0 zoom, annotation
|
||||
copy/paste (Ctrl+C/V), Ctrl+Delete deletes the step, Shift+arrows
|
||||
fast-nudge — plus a shortcuts reference dialog.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Renderer scripts no longer collide in the shared global scope (the app
|
||||
previously failed to boot with a blank window).
|
||||
- Focused-view toggle persists correctly (`step.focusedView.enabled`).
|
||||
- Annotation style edits no longer steal input focus on each keystroke.
|
||||
- Step list stays in sync after saves and undo/redo.
|
||||
- Escape deselects the active annotation instead of deleting it.
|
||||
- Modal dialogs (confirm/prompt/etc.) no longer resolve as cancelled when
|
||||
an action button is clicked — `openModal`'s teardown was firing the
|
||||
dialog's default-cancel callback before the button's own resolution
|
||||
could win. This was most visible as the step "Delete" button silently
|
||||
doing nothing.
|
||||
- New Capture no longer hides the app window ~1.2s after starting; a
|
||||
session now starts paused and the window only tucks away once the user
|
||||
presses "Start recording" in the capture bar, so the app doesn't vanish
|
||||
out from under you.
|
||||
- The capture status bar (REC count / Shoot / Auto / Pause / Finish) is
|
||||
now shown only in the editor view; it no longer appears over the
|
||||
library when a session is still running in the background.
|
||||
- Click-triggered captures now grab the cursor position at the instant of
|
||||
the click (instead of from the cache's last refresh, up to ~75ms
|
||||
earlier) and use it for the click-marker placement, and the
|
||||
click-capture cache is armed as soon as recording starts so the very
|
||||
first click is captured instantly.
|
||||
|
||||
### Added (initial feature set)
|
||||
|
||||
- Guide library with folders, favorites, title + full-text search, and a
|
||||
quick-actions palette.
|
||||
- Capture engine: full-screen / active-window / region capture, delay,
|
||||
pause/resume, global hotkeys, click markers, clipboard paste, image import.
|
||||
- Three-pane editor: step tree with substeps, statuses, hidden/skipped steps,
|
||||
focused view, autosave, undo/redo.
|
||||
- Annotation canvas: rect, oval, line, arrow, text, tooltip, numbered marker,
|
||||
blur, highlight, magnify, crop; normalized JSON scene graph.
|
||||
- Rich text descriptions, informational text blocks, code blocks, tables,
|
||||
step links, and placeholders (global / guide / system scope).
|
||||
- Single-file `.sfgz` share archives, linked guides with lock files,
|
||||
snapshot backups and restore.
|
||||
- Exporters: JSON, Markdown, Simple HTML, Rich HTML, PDF, animated GIF,
|
||||
image bundle, DOCX, PPTX; per-format templates shareable as `.sfglt`.
|
||||
- System/light/dark theming, keyboard shortcuts, settings dialog.
|
||||
- Offline guarantee: zero network code paths.
|
||||
@@ -0,0 +1,24 @@
|
||||
# Code of Conduct
|
||||
|
||||
## Our Standard
|
||||
|
||||
Everyone participating in this project — contributors, reviewers, and users
|
||||
filing issues — is expected to:
|
||||
|
||||
- be respectful and constructive; critique code, not people;
|
||||
- assume good faith and ask before escalating;
|
||||
- keep discussions on-topic and free of harassment, discrimination, or
|
||||
personal attacks;
|
||||
- respect the clean-room rules in CONTRIBUTING.md when discussing other
|
||||
products.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Maintainers may edit, hide, or remove comments, commits, issues, and PRs that
|
||||
violate this standard, and may temporarily or permanently ban contributors
|
||||
for repeated or egregious behavior.
|
||||
|
||||
## Reporting
|
||||
|
||||
Report unacceptable behavior privately to the maintainers via the contact
|
||||
listed on the project page. Reports are handled confidentially.
|
||||
@@ -0,0 +1,84 @@
|
||||
# Contributing
|
||||
|
||||
Thanks for improving StepForge.
|
||||
|
||||
## Before You Start
|
||||
|
||||
- Open or link the issue that describes the work.
|
||||
- Keep the change small and focused.
|
||||
- If the work does not have an issue yet, create one first so the PR can
|
||||
reference it.
|
||||
|
||||
## Clean-Room Rules
|
||||
|
||||
StepForge is an independent reimplementation of publicly documented
|
||||
guide-capture workflow patterns. To keep it legally clean:
|
||||
|
||||
- Do **not** use the names, logos, icons, screenshots, or UI strings of
|
||||
commercial documentation products anywhere in code, assets, or docs.
|
||||
- Do **not** copy wording from other products' documentation into the UI.
|
||||
- Do **not** decompile, disassemble, or otherwise inspect proprietary
|
||||
binaries to derive behavior.
|
||||
- Implement behavior from public descriptions and your own design only.
|
||||
- Keep file formats (`.sfgz`, `.sfglt`, guide/step JSON) documented and
|
||||
versioned in `docs/` and `ARCHITECTURE.md`.
|
||||
|
||||
Contributions are accepted under MPL-2.0 with a Developer Certificate of
|
||||
Origin sign-off (`git commit -s`).
|
||||
|
||||
## Offline Rules
|
||||
|
||||
- No network code paths in the application. No telemetry, update checks,
|
||||
license checks, remote fonts, or remote APIs — ever.
|
||||
- No new runtime dependencies without prior maintainer agreement; prefer
|
||||
internal implementations using Node built-ins.
|
||||
|
||||
## Branching
|
||||
|
||||
- Use a branch name that includes the issue number, such as
|
||||
`issue-123-update-readme`.
|
||||
- Keep unrelated cleanup in a separate branch, only have the fix in the
|
||||
branch.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
- Every pull request must reference an issue number in the body with
|
||||
`Closes #123`, `Fixes #123`, or `Relates to #123`.
|
||||
- Summarize the change clearly and call out anything a reviewer should
|
||||
verify manually.
|
||||
- Update docs when behavior changes, and add a CHANGELOG entry for every
|
||||
user-visible change.
|
||||
- Every exporter or storage change **requires tests**; output changes
|
||||
require updated snapshot fixtures under `tests/fixtures/`.
|
||||
|
||||
## Tests
|
||||
|
||||
Run the local checks before opening or updating a PR:
|
||||
|
||||
```bash
|
||||
bash tests/run_test.sh
|
||||
```
|
||||
|
||||
Put new shell checks in `tests/checks/` so the shared runner picks them up
|
||||
automatically. The shell checks invoke the `node --test` workflow suites in
|
||||
`tests/unit/`.
|
||||
|
||||
Write tests that exercise **real workflows and verify actual output** —
|
||||
create a guide, export it, parse the bytes that came out — rather than
|
||||
grepping for magic strings.
|
||||
|
||||
The Gitea workflow in `.gitea/workflows/tests.yaml` runs the same command
|
||||
automatically on pushes and pull requests.
|
||||
|
||||
Please add lots of tests to each of your PR's and be descriptive with the
|
||||
tests so that the issue doesn't happen again or the feature doesn't get
|
||||
overwritten.
|
||||
|
||||
## Review Checklist
|
||||
|
||||
- The PR is linked to the correct issue.
|
||||
- The test suite passes locally.
|
||||
- Any relevant docs or comments are updated.
|
||||
- The change stays within the intended scope.
|
||||
- The PR body explains any manual verification that is still needed.
|
||||
- No network calls, no new dependencies, no trademarked assets.
|
||||
@@ -0,0 +1,91 @@
|
||||
# Getting Started
|
||||
|
||||
StepForge is a fully offline desktop app. Nothing is uploaded or synced, and
|
||||
all guides stay on your machine.
|
||||
|
||||
## 1. Install
|
||||
|
||||
From the repository root:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
That installs Electron and the local packaging tools used by the scripts.
|
||||
|
||||
## 2. Launch the app
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
The first launch creates the local StepForge data directory. On Linux it is
|
||||
usually under `~/.local/share/stepforge`. On Windows it is usually under
|
||||
`%APPDATA%/stepforge`.
|
||||
|
||||
## 3. Create your first guide
|
||||
|
||||
In the library view:
|
||||
|
||||
1. Click `New guide`.
|
||||
2. Give the guide a clear title.
|
||||
3. Open the guide to enter the editor.
|
||||
|
||||
You can also import a guide archive with `Import archive` if you already have
|
||||
one.
|
||||
|
||||
## 4. Add content
|
||||
|
||||
There are two simple ways to start:
|
||||
|
||||
1. Import screenshots with the `Import` button in the editor.
|
||||
2. Paste an image from the clipboard if you already copied one.
|
||||
|
||||
If you want to capture new screenshots, open `Quick` actions and start a
|
||||
capture session. Use `Settings` to set the capture hotkey and other capture
|
||||
options.
|
||||
|
||||
## 5. Edit the guide
|
||||
|
||||
The editor is split into three panes:
|
||||
|
||||
1. Steps on the left
|
||||
2. Canvas in the center
|
||||
3. Properties on the right
|
||||
|
||||
Use the canvas tools to add shapes, arrows, text, blur, highlight, numbers,
|
||||
and crops. Use the right pane to edit the step title, description, and
|
||||
annotation details.
|
||||
|
||||
## 6. Save and export
|
||||
|
||||
Use these actions from the top bar:
|
||||
|
||||
1. `Save` writes the guide to disk.
|
||||
2. `Export` opens format choices such as JSON, Markdown, HTML, PDF, GIF,
|
||||
image bundle, DOCX, and PPTX.
|
||||
3. `Linked` shows archive details when a guide is linked to a shared `.sfgz`
|
||||
file.
|
||||
|
||||
If you want to find commands quickly, press `Ctrl+/` for Quick Actions.
|
||||
|
||||
## Useful shortcuts
|
||||
|
||||
1. `Ctrl+/` opens Quick Actions
|
||||
2. `Ctrl+S` saves the current guide
|
||||
3. `Ctrl+Z` undoes the last edit
|
||||
4. `Ctrl+Shift+Z` redoes the last edit
|
||||
5. `Alt+Up` and `Alt+Down` move the selected step
|
||||
|
||||
## If something is missing
|
||||
|
||||
1. Open `Settings` to review capture, export, and editor options.
|
||||
2. Run `npm run sample` to generate a sample guide and exported examples.
|
||||
3. Run `bash scripts/verify.sh` to check the full offline workflow.
|
||||
|
||||
## Optional builds
|
||||
|
||||
1. `bash scripts/build-release.sh` assembles the offline release layout.
|
||||
2. `npm run package:windows` creates the portable Windows `.exe` in
|
||||
`releases/`.
|
||||
3. `bash scripts/package-linux.sh` creates Linux release artifacts.
|
||||
@@ -0,0 +1,69 @@
|
||||
# Security
|
||||
|
||||
## Offline Guarantee
|
||||
|
||||
StepForge ships with **zero network code paths**. The application:
|
||||
|
||||
- opens no sockets and performs no HTTP requests,
|
||||
- has no telemetry, crash reporting, or analytics,
|
||||
- performs no update checks and no license validation,
|
||||
- has no account system, cloud sync, or remote AI integration,
|
||||
- embeds no remote fonts, CDNs, or external references in exports.
|
||||
|
||||
The only network activity in the project's lifetime is the one-time
|
||||
development fetch of the Electron shell via npm (see
|
||||
`build/agent_audit.md`). The packaged app never goes online.
|
||||
|
||||
## Threat Model
|
||||
|
||||
Because the app is local-only, the realistic attack surface is **malicious
|
||||
files opened by the user**:
|
||||
|
||||
### Archive imports (`.sfgz`, `.sfglt`)
|
||||
|
||||
Both formats are zip files. The reader in `core/zip.js` validates every entry
|
||||
before extraction:
|
||||
|
||||
- entry names must be relative, must not contain `..` segments, drive
|
||||
letters, or absolute paths;
|
||||
- entries are extracted only beneath the destination directory (resolved
|
||||
path is verified to stay inside it);
|
||||
- file sizes are taken from actual inflated data, not trusted headers;
|
||||
- unknown entries outside the documented layout are ignored.
|
||||
|
||||
### Image imports (PNG/JPEG/GIF)
|
||||
|
||||
Imported images are decoded by the platform image codecs in the Electron
|
||||
shell and re-encoded to PNG before storage. The pure-JS PNG decoder in
|
||||
`core/png.js` (used by exporters) rejects malformed dimensions, oversized
|
||||
allocations, and bad CRCs.
|
||||
|
||||
### Linked guides and lock files
|
||||
|
||||
Shared `.sfgz` files opened in *linked mode* use a sidecar lock file
|
||||
(`<name>.lock-sfgz`) containing the holder's machine name and timestamp.
|
||||
This is an advisory lock for coordination on shared folders, **not** a
|
||||
security boundary: a hostile or crashed peer can delete it. Conflicts are
|
||||
surfaced to the user with keep-editing / discard options; the format is
|
||||
last-write-wins and that risk is documented in the UI.
|
||||
|
||||
### Local data at rest
|
||||
|
||||
The guide store is **not encrypted at rest** — it inherits the user account's
|
||||
filesystem protections, like any document folder. If you need encrypted
|
||||
sharing, encrypt the `.sfgz` with external tooling; native encrypted archives
|
||||
are a tracked enhancement, not a current feature.
|
||||
|
||||
## Renderer Hardening
|
||||
|
||||
The Electron renderer runs with `contextIsolation: true` and
|
||||
`nodeIntegration: false`; the only privileged surface is the explicit
|
||||
allowlisted IPC API in `app/preload.js`. Guide description HTML is sanitized
|
||||
(allowlisted tags/attributes, no scripts, no event handlers, no external
|
||||
URLs) before storage and again before rendering or export.
|
||||
|
||||
## Reporting
|
||||
|
||||
Report vulnerabilities by opening an issue marked `security` (this is a
|
||||
local-only tool, so coordinated disclosure pressure is low; do not include
|
||||
exploit archives directly — describe the structure instead).
|
||||
Reference in New Issue
Block a user