Cleanup
Template tests / tests (push) Successful in 1m48s

This commit is contained in:
Iisyourdad
2026-06-11 09:44:52 -05:00
parent 99376fdeb2
commit dca3e042f2
26 changed files with 53 additions and 53 deletions
+122
View File
@@ -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.
+93
View File
@@ -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.
+24
View File
@@ -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.
+84
View File
@@ -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.
+91
View File
@@ -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.
+69
View File
@@ -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).