Commit Graph
95 Commits
Author SHA1 Message Date
Tyler 68e886c142 Fix zoom
Template tests / tests (push) Successful in 1m52s
2026-07-07 12:59:55 -05:00
Tyler 9555db9bf5 Fix
Template tests / tests (push) Failing after 4m1s
2026-07-07 12:04:08 -05:00
Tyler a55a7a9170 fix text box editing and zoom shortcuts 2026-07-07 10:13:55 -05:00
TylerandClaude Fable 5 c1ccb5739b Add optimistic revisions, keep dirty state on failed saves, quarantine corrupt data
Template tests / tests (pull_request) Failing after 33s
Phase 1 of the improvement plan (PR 4 of the sequence): stop concurrent
whole-object saves from losing edits, stop failed saves from reporting clean,
and stop corrupt user data from silently vanishing.

Revisions (core/schema.js, core/store.js):
- Every guide and step carries a monotonic `revision`, bumped on each store
  write. Legacy v1 data without the field reads as revision 0 and upgrades on
  its next save — no migration pass, no data rewrite.
- saveGuide/saveStep accept { expectedRevision } for compare-and-swap saves;
  a mismatch throws RevisionConflictError instead of clobbering. Direct user
  edits pass no expectation (the user is the authority); background writers
  must pass one.

Stale AI responses (app/text-intel.js):
- generateStepPatch snapshots the step revision before the (slow) model call,
  re-reads the step after it, and saves with the original expectedRevision. A
  user edit made during generation now surfaces as "the step changed while AI
  was generating; nothing was overwritten" — previously the AI response
  silently overwrote the newer edit.

Autosave truthfulness (app/renderer/editor.js):
- flushStep/flushGuide cleared the dirty flag BEFORE awaiting the IPC save,
  so a rejected save (invoked via a debounce that never handled rejections)
  lost the visible dirty state. The flag is now cleared only after a durable
  save; failures keep it dirty, surface a persistent saveError in editor
  meta, toast the user, and retry on the next edit or explicit save.
- Navigating away from the editor flushes pending debounced saves so the
  last edit can never be dropped by a view switch.

Corruption quarantine (core/store.js):
- listGuides/listSteps used to silently skip unreadable entries — a corrupt
  guide just vanished from the library. Corrupt guide/step directories are
  now moved to library/quarantine (original bytes preserved) and recorded in
  a recovery report (store.getRecoveryReport()) for the UI. Empty in-progress
  directories are still skipped quietly — absence of guide.json is not
  corruption.

Tests: revision increments, stale-CAS rejection with user edit surviving,
CAS success path, guide CAS, v1 no-revision upgrade, guide/step quarantine
with preserved bytes + recovery report, empty-dir non-quarantine, AI
stale-write rejection end-to-end (user edit mid-generation survives) and the
clean-apply path. 240 unit tests pass; startup smoke and sample-artifact
E2E pass.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-03 22:57:50 -05:00
TylerandClaude Fable 5 ccbb9b03dc Enforce a truthful local-first AI/privacy contract
Template tests / tests (pull_request) Failing after 34s
Phase 1 of the improvement plan (PR 3 of the sequence). The docs claimed
"fully offline"/"never talks to the network," but text-intel makes HTTP
requests to a configurable Ollama host that could be remote, with no timeout,
no cancellation, and no size limit; the Windows hook logged raw keystrokes
into capture metadata that could then be sent to that host.

Privacy — raw keystroke capture:
- New capture.captureTypedText setting, default false. With it off, printable
  characters are never buffered in JS and the Windows keyboard hook never even
  emits them across the process boundary (the flag is threaded into the C#).
  Shortcut/navigation detection (Ctrl+T, Enter, …) is unaffected.

AI network hardening (app/text-intel.js):
- Every Ollama call goes through fetchJson with an AbortController deadline
  (ai.timeoutMs, default 60s): a dead endpoint fails fast instead of leaving
  UI actions pending forever.
- Cancellation: in-flight requests are tracked and cancelInflight(guideId)
  aborts them; new ai:cancel IPC + api.ai.cancel are called when the editor
  closes, and shutdown cancels everything.
- Bounded concurrency (2) for AI network work.
- Screenshots are only attached when allowed (ai.attachScreenshots), the model
  is vision-capable, and the image is within ai.maxImageBytes — no more
  unbounded base64-expanded 4K bodies.

Local-first host policy (core/text-intel.js):
- New isLoopbackHost + validateOllamaHost. By default only a loopback Ollama
  endpoint is contacted; a remote host is refused with a clear message unless
  ai.allowRemoteHost is explicitly enabled. Blocked hosts are never contacted.

Honest documentation:
- README, package.json, and the welcome screen drop "fully offline"/"never
  talks to the network"/"Electron is the only dependency" for an accurate
  local-first contract that discloses the optional AI path and the bundled
  Tesseract OCR dependency.
- New docs/PRIVACY.md details exactly what is collected locally and the one
  outbound (opt-in, loopback-by-default) AI feature.

Tests: loopback/remote host matrix, remote-blocked-without-opt-in (and never
contacted), remote-allowed-with-opt-in, request timeout, explicit cancel vs
timeout, typed-text off-by-default vs opted-in, shortcut detection still works,
and a source guard that the C# CHAR emission stays behind the opt-in. 224 unit
tests pass; startup smoke and workflow E2E pass.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-03 11:36:01 -07:00
TylerandClaude Fable 5 50e445e7c3 Close the renderer privilege boundary: navigation, IPC, permissions, shell
Template tests / tests (pull_request) Failing after 33s
Phase 1 of the improvement plan (PR 2 of the sequence). A remote page could
previously inherit the privileged preload bridge: the main window had no
navigation guard or popup handler, IPC handlers accepted any sender, every
Electron permission was granted to everyone, and shell:openPath accepted an
arbitrary renderer-supplied target.

- New app/security.js (plain-Node testable) centralizes the policy:
  app-page identity, navigation/popup denial, deny-by-default permissions,
  external-URL validation, IPC sender guard, payload budgets, field
  validators, and a produced-files registry.
- Every window (main, region overlay, capture worker) is sandboxed, denies
  all navigation away from its own page, denies every popup, and refuses
  webview attachment.
- Every IPC channel now rejects events that are not from the main window's
  top frame on index.html, rejects non-plain/oversized argument bags, and
  channels with risky inputs validate ids, enums, names, and sizes
  (path-traversal and prototype-pollution guards included).
- Permissions are deny-by-default; the only grant in the app is display
  capture (+media) for the dedicated capture-worker page. The display-media
  handler also verifies the requesting frame.
- shell:openPath/showItemInFolder are gone. Replacements are intent-specific:
  openProduced (only files the main process produced this session),
  revealLinkedArchive (path read from the store, not the renderer), and
  openExternal (parsed, scheme-checked http(s)/mailto only).
- archive:peek removed: unused, and it let the renderer read arbitrary
  archives by path.
- export:run only accepts output directories that came from a dialog pick or
  remembered settings; anything else re-prompts.
- Renderer: description links never navigate; http(s)/mailto open externally
  via the validated handler.
- 12 new security regression tests: hostile navigation targets, permission
  matrix, sender spoofing (wrong window/subframe/navigated/disposed frames),
  oversized payloads, traversal/pollution attempts, produced-file registry,
  and source-level guards (no blanket grants, no generic shell channels,
  sandbox on every window).

Verified: 215 unit tests pass; startup smoke, unit-workflows, sample
artifacts, and build-release E2E pass; click self-test still reaches
source: stream with markers 3/3 and burst 8/8 under the deny-by-default
policy (arm/debounce remain the known pre-existing failures, untouched
here); UI screenshot confirms the sandboxed renderer boots.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-03 11:25:36 -07:00
Tyler 7c006a7bb7 Fix Windows installer and build versioning 2026-06-26 22:37:13 -05:00
Tyler 266a92fedb Fix windows breakage from linux dev 2026-06-26 18:38:03 -05:00
Tyler 0325b6efbc ubuntu not working. idk I'm just gonna use windows anyway 2026-06-26 18:00:22 -05:00
Tyler 3c5c520799 work on linux release
Linux release not fully working as well as windows, will investigate later
2026-06-26 17:02:35 -05:00
Tyler 962f929de2 fixed recording workflow on ubuntu
ai gen description: Root cause 1 — The mandatory constraint removal (most critical)

In Electron 29+ / Chromium 116+, the old getUserMedia format with a mandatory: {} wrapper was removed. The capture worker was still using it:

video: { mandatory: { chromeMediaSource: 'desktop', ... } }  // broke in Electron 29+
This caused getUserMedia to throw in the worker, which sent a stream-error back to the main process, which fell back to the frame loop. Fix: flat constraint format with no mandatory wrapper.

Root cause 2 — Frame loop spammed getSources() every 200ms

Once the stream backend failed, the fallback frame loop called desktopCapturer.getSources() 5× per second. On Linux via the XDG portal, each getSources() call is a new permission request → dialog every few seconds. Fix: on Linux without click capture, don't fall back to the frame loop at all — just let interval/hotkey captures take individual fresh shots.

Root cause 3 — Interval/hotkey captures bypassed the stream backend

Even when the stream backend is running, sessionCapture('interval') went straight to shoot() → grab() → getSources(). Fix: non-click triggers now pull a buffered frame from the stream backend's ring buffer (sampled every 100ms), completely avoiding getSources().

Root cause 4 — Stream backend never started on Wayland

The earlier fix made recorderWanted = false on Wayland, so the stream backend never even started. Fix: recorderWanted is now true whenever stream capture is enabled (the default), regardless of whether click detection is available.

Safety net — setPermissionCheckHandler

Electron 29+ requires an explicit permission grant for display-capture in renderer windows. Added a handler that grants all permissions (safe for a fully local/offline app like StepForge).

Windows is completely unaffected — the constraint fix is Electron-version level (affects both platforms the same), and all the Linux-specific frame-loop guards only fire when clickCaptureAvailable() returns false, which only happens on Wayland.
2026-06-26 16:11:16 -05:00
Tyler a5a8498e71 Keep recording bar in ubuntu 2026-06-26 15:49:49 -05:00
Tyler 3915865029 Keep ai model saved when app closes. 2026-06-26 08:48:38 -05:00
Tyler a971ff870f Added feature to where local ai models can see the image in each step. 2026-06-26 08:05:18 -05:00
TylerandClaude Sonnet 4.6 ebeb6ac578 label Enable AI setting as experimental
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-24 12:04:40 -05:00
TylerandClaude Sonnet 4.6 f60cf1a688 label generate-all-steps menu item as experimental
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-24 12:02:59 -05:00
TylerandClaude Sonnet 4.6 2d1b474931 fix generate-all-steps: never overwrite existing content, right target per step
The previous version called target:'all' on every step unconditionally,
which caused the AI to rewrite steps that already had good titles and
descriptions — often making them worse or inaccurate.

New behaviour:
- Build a queue of only the steps that are actually missing content
  (placeholder title and/or empty description).
- Determine target per step: 'title', 'description', or 'all' — only
  fill what is blank; leave existing user-written text completely alone.
- If all steps already have titles and descriptions, show a toast and stop.
- Call reload() once at the end instead of patching this.steps mid-loop,
  so the editor and step list both update atomically from the store.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-24 11:27:24 -05:00
TylerandClaude Sonnet 4.6 c60b7247af generate all text fields with AI now fills every step, not just current
Previously the menu action only ran AI generation on the selected step.
Now it loops through all steps sequentially, showing progress ("AI:
filling step N of M…"), then reloads the visible step when done.
Partial failures are reported in the final toast.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-24 11:22:27 -05:00
TylerandClaude Sonnet 4.6 162ae0ae05 improve AI rewrite UX and title quality
**AI button: dynamic tooltip hints**
- titleAiBtn and descAiBtn now show "Rewrite with AI" when the field
  already has user content, "Generate with AI" when empty.
- updateAiButtonHints() fires on every title/description input event
  and whenever syncStepFields() runs to keep hints current.

**Stronger rewrite prompt**
- When the user has typed a draft title or description, the prompt now
  shows it explicitly: "User's draft title (rewrite this): '...'"
- Rules changed from "improve its wording" to "Your only job is to
  polish its grammar and phrasing. Do NOT replace it with something
  different." — prevents the model from ignoring the user's text and
  generating fresh content from capture context.
- The suggested-title hint is suppressed when a draft title exists so
  the model doesn't silently swap the user's text for the auto-title.

**Title quality: generic window title filter**
- GENERIC_WINDOW_TITLES Set filters "New Tab", "Untitled", "Loading"
  etc. from the window-title path so they no longer produce titles
  like "Open New Tab in Chrome".

**Title quality: app name stripping for non-browser apps**
- stripBrowserNameSuffix now accepts an optional appName; it strips
  the app's display name and process name from the window title suffix
  using the same pattern as browser names.
- "Document1.docx - Word" with appName "winword" → "Document1.docx".
- buildCaptureTitle passes metadata.appName into the strip call.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-24 10:42:56 -05:00
TylerandClaude Sonnet 4.6 68ceee83ef fix ai title generation and remove separate rewrite section
- Remove the standalone "AI Rewrite" section from the editor panel.
  The existing title and description AI buttons already flush the step
  first, so they rewrite whatever the user has typed in those fields.

- Never pass a generic fallback title ("Screen capture", "Window
  capture", "Region capture", "Capture") as the AI title candidate or
  as step content. It is now treated as "(not set — generate a
  specific action title)" so the AI always produces something real.

- hasRichCaptureContext now counts any non-trivial app name or window
  title as sufficient context, instead of requiring non-browser noise.

- Prompt rules updated: "NEVER output Screen/Window/Region capture",
  separate paths for improving a user draft vs generating from context,
  and clearer guidance when context is limited (use app/window name).

- isPlaceholderTitle helper guards summarizeStepForAi so a
  default-titled step presents itself as empty to the AI.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-24 09:27:23 -05:00
TylerandClaude Sonnet 4.6 d244626583 ai smart auto-documentation and text rewrite
- Store captureMetadata (OCR text, window/app/element info) with each
  step at capture time so AI always has the original rich context.
- Add buildCaptureContext() to TextIntelService; capture.js uses it
  instead of buildCaptureTitle() so both title and metadata come from
  one pass.
- generateStepPatch() prefers stored captureMetadata over re-running
  OCR, giving the AI the best possible context when the user clicks
  an AI button later.
- Add autoDoc setting: when enabled every capture (shoot, region, and
  session hotkey/click) is automatically documented by AI. Manual
  captures await AI before returning; session captures fire-and-forget
  and push a step:updated event so the renderer reloads seamlessly.
- Add ai:rewriteText IPC and rewriteText() method for plain-text
  polishing via a separate callOllamaText() that skips JSON mode.
- Add "AI Rewrite" section in the editor right panel: textarea + AI
  button that rewrites whatever the user types in place.
- Improve buildAiPrompt() rules: action-focused title instructions,
  explicit anti-junk rules (no "Capture the screen / OCR" blocks),
  and a context-quality gate that suppresses blocks when context is
  thin.
- Add autoDoc checkbox to AI settings dialog.
- Renderer handles step:updated to reload the selected step after
  background auto-doc finishes.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-24 09:17:31 -05:00
Tyler 1a1dbb846b fix settings AI test connection wiring 2026-06-23 18:14:12 -05:00
Tyler 33eb0f745e semantic titles ai assist 2026-06-23 17:46:33 -05:00
Tyler d89abf5b78 Arm capture when creating a guide from the library
createGuide() opened the new guide with plain openGuide(), so no capture
session was armed — the "Start recording" bar never bound to the new guide,
and clicking it did nothing (or resumed a stale prior session). Every other
open path (guide cards, search results, New Capture) arms a paused session;
this aligns createGuide() with them via openGuideAndArmCapture().
2026-06-16 13:13:32 -05:00
Tyler 5edf740fda Fix capture degrading in eco/power-saving mode; decouple from display refresh rate
Template tests / tests (push) Successful in 1m47s
Two changes:
- Hold powerSaveBlocker.start('prevent-app-suspension') for the duration of
  each recording session. Windows Power Throttling (EcoQoS) can CPU-starve
  the capture-worker renderer mid-session, causing stream frame requests to
  time out and degrade to the slower in-process legacy loop. The blocker
  signals to the OS that this is an active workload and keeps the stream
  worker and main-process event loop running at full speed. Released on
  pause and finish.
- Remove maxFrameRate: 30 from the getUserMedia constraint in the capture
  worker. The actual sampling rate is driven entirely by the setInterval
  timer, so the stream's native frame rate is irrelevant to capture
  correctness. Capping at 30 could silently reduce stream delivery on some
  drivers when the display itself runs at ≤30 Hz (common in power-saving
  mode), leaving the ring buffer with fewer fresh frames at click time.
2026-06-15 18:20:57 -05:00
Tyler 46d0d622ca Redesign hotkey settings as press-to-record keycap widgets
Template tests / tests (push) Successful in 1m44s
Replace the raw accelerator-string text inputs in Settings > Capture
with a polished "click and press a key combination" control that
renders the shortcut as keycap chips with a clear button.
2026-06-15 17:59:08 -05:00
TylerandClaude Sonnet 4.6 204be178a3 Fix cramped spacing of the Quick Actions search hint
Template tests / tests (push) Successful in 1m42s
The "Type to search, arrows to move, Enter to open." hint had no
margin, so it sat flush against the search input above while the
results list below it had a 10px margin + 8px padding before its
divider - lopsided spacing. Make .quick-actions a flex column with a
uniform 8px gap so the hint sits evenly between the input and the
results divider.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-15 17:39:39 -05:00
TylerandClaude Sonnet 4.6 5b54305b9f Show a loading spinner on the Export/Preview buttons while exporting
The export dialog's Export and Preview buttons now disable and show a
spinner with status text while the corresponding async operation is
in flight, so the user knows the (sometimes slow) export is running
and the app isn't stuck.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-15 17:07:35 -05:00
Tyler b84b7883fe Fix export dialog format handling 2026-06-15 14:03:32 -05:00
Tyler b85eaad0cb Indent nested guide editor steps 2026-06-15 14:02:34 -05:00
Tyler 7229b154c9 Merge branch 'main' into export_changes 2026-06-15 13:11:35 -05:00
TylerandClaude Sonnet 4.6 722c5d2eee Add a Description field to the Guide information dialog
Reuses the guide's existing descriptionHtml/descriptionText, which
already renders on the PDF cover and at the top of every other export
format, so it surfaces alongside author/co-authors/organization with
no exporter changes needed.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-15 13:04:35 -05:00
TylerandClaude Sonnet 4.6 0b490e2aa9 Add guide metadata, redesign PDF cover, paginate steps per-page, and run exports in a helper process
Lets users record author/co-authors/organization for a guide via a new
"Guide information…" dialog; this metadata renders below the title on
the PDF cover (title now sits above the accent rule). PDF export also
paginates so each step fits its own page where possible, keeps a
step's title/image/lead-in together, and forces the next step onto a
fresh page after an oversized step overflows. Exports now run in a
forked helper process so large guides no longer freeze the UI.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-15 12:54:31 -05:00
Tyler e588f93a19 Merge pull request 'Mass guide editor changes' (#18) from guide_editor_changes into main
Template tests / tests (push) Successful in 1m39s
2026-06-15 15:37:48 +00:00
Tyler db2382294e Document the Undo/Redo shortcut in the keyboard shortcuts help
Template tests / tests (push) Successful in 1m41s
Template tests / tests (pull_request) Successful in 1m40s
Now that undo also restores deleted steps, it's worth surfacing the
existing Ctrl+Z / Ctrl+Shift+Z shortcut to users.
2026-06-15 10:26:56 -05:00
Tyler 58b4182c54 Add global undo for step deletion; simplify capture session UI
Template tests / tests (push) Successful in 1m41s
Template tests / tests (pull_request) Successful in 1m39s
- The canvas Undo/Redo buttons now also undo/redo step deletion
  (single or multi-select), restoring the step's data, images, and
  position in the order. Backed by a new step:restore IPC call and
  GuideStore.restoreStep().
- Remove "Finish capture session" from the capture menu. The
  top-right recording bar in the guide editor is now the only place
  to start/stop recording, with its toggle relabeled
  "Start recording" / "Stop recording".
2026-06-15 10:15:15 -05:00
Tyler 88d1fc3efb Spell out "Highlight" in the annotation toolbar instead of "Hi"
Template tests / tests (push) Successful in 1m39s
Template tests / tests (pull_request) Successful in 1m44s
2026-06-15 09:50:34 -05:00
Tyler 2bb17b170e Use display labels for annotation types in the Annotations list and copy-style text
Template tests / tests (push) Successful in 1m40s
Template tests / tests (pull_request) Successful in 1m40s
The per-annotation list cards and the "Copy this style to every other
... annotation" text/tooltips were still showing raw type ids (rect,
arrow, etc). Use ANNOTATION_TYPE_LABELS for these too.
2026-06-15 09:46:31 -05:00
Tyler 6408040623 Show proper display names in the annotation Type dropdown
Template tests / tests (push) Successful in 1m39s
Template tests / tests (pull_request) Successful in 1m39s
The annotation editor's "Type" select used the raw lowercase type id
(e.g. "rect") as its label. Add an ANNOTATION_TYPE_LABELS map so it
shows "Rectangle", "Tooltip", "Number", etc. instead.
2026-06-15 09:43:09 -05:00
Tyler a3425c36b4 Spell out "Rectangle" in tool labels instead of "Rect"
Template tests / tests (push) Successful in 1m41s
Template tests / tests (pull_request) Successful in 1m39s
Keeps the internal 'rect' annotation type id (canvas/raster/schema)
unchanged; only updates the user-facing toolbar button and shortcuts
help text.
2026-06-15 09:38:24 -05:00
Tyler 5d4925dee4 Fix export block ordering, stale per-step UI, and cross-step block loss
Template tests / tests (push) Successful in 1m42s
Template tests / tests (pull_request) Successful in 1m41s
Exporters now interleave text/code/table blocks in the same order they
appear in the editor's Blocks panel (via a shared stepContentGroups
helper) instead of grouping by kind, so exported docs match the guide
editor's ordering.

selectStep() now also refreshes the Focused View controls and Blocks
panel (previously only done by renderAll), so switching steps no longer
leaves the previous step's blocks/focused-view sliders on screen. It
also flushes any pending edits on the outgoing step before switching, so
a later guide-wide reload (e.g. applying an annotation style to the
whole guide) can't discard unsaved text-block edits on other steps.
2026-06-14 09:11:00 -05:00
TylerandClaude Sonnet 4.6 fee394f6b7 Add quote indicator and distinct callout styling for Note/Tip/Warning/Important
Template tests / tests (push) Successful in 1m39s
Template tests / tests (pull_request) Successful in 1m42s
- Description editor: blockquotes get a left accent line and muted color so
  it's clear when typing in "quote mode" (matches the existing active Quote
  toolbar button highlight).
- Editor block cards: each text block's left border is colored by its level
  (info/success/warn/error) so Note/Tip/Warning/Important are distinguishable
  at a glance.
- PDF/HTML/DOCX exports: callouts now get a level-specific accent color and
  tinted background/shading (blue/green/amber/red), instead of all looking
  identical except for the label text.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-13 23:02:03 -05:00
Tyler 420b32c0f8 Description editor: insert link placeholder inline instead of prompting
Template tests / tests (push) Successful in 1m39s
Template tests / tests (pull_request) Successful in 1m40s
Clicking Link no longer pops up "Link text"/"Link URL" dialogs; it
inserts [Text](Link) (or [selectedText](Link)) directly so the
placeholders can be edited in place.
2026-06-13 21:11:58 -05:00
Tyler bb959a4bb9 Description editor: preserve newlines on export, toggle Quote, highlight active toolbar buttons
Template tests / tests (push) Successful in 1m49s
Template tests / tests (pull_request) Successful in 1m38s
- Enter now starts a new <p> (defaultParagraphSeparator) so line breaks
  in the description box show up as line breaks in HTML/Markdown/Confluence
  exports; htmlToMarkdown also handles <div>-separated paragraphs.
- Clicking Quote while already in a blockquote toggles back to a paragraph.
- Toolbar buttons (Bold, Italic, Bullet, Number, Quote) turn blue to
  reflect the formatting state at the current cursor/selection.
2026-06-13 21:08:31 -05:00
Tyler 46d12cc829 Insert markdown-style [text](url) links from the Link toolbar button
Template tests / tests (push) Successful in 1m40s
Template tests / tests (pull_request) Successful in 1m40s
Previously this wrapped the selection in a real <a> tag via
createLink. Now it inserts literal [text](url) markdown syntax,
using the selection as the link text or prompting for it if nothing
is selected.
2026-06-13 20:47:16 -05:00
Tyler 8e2e1f41fb Show "Make substep of…" as a hover submenu listing all steps
Template tests / tests (push) Successful in 1m39s
Template tests / tests (pull_request) Successful in 1m40s
Replaces the number-entry prompt with a scrollable side panel (so
large guides don't overflow the screen) listing every eligible step;
clicking one immediately re-parents the step. Context menus now
support submenu items in general via contextMenu().
2026-06-13 20:33:29 -05:00
Tyler a58607f029 Add a "Make substep of…" option to the step context menu
Template tests / tests (push) Successful in 1m40s
Template tests / tests (pull_request) Successful in 1m39s
Lets a step be re-parented under another step by entering that step's
displayed number, moving it (with its own substeps) to the end of the
target's substeps and updating the numbering accordingly.
2026-06-13 20:23:31 -05:00
Tyler be88434e44 Prompt for a name and save before creating a manual snapshot
Template tests / tests (push) Successful in 1m38s
Template tests / tests (pull_request) Successful in 1m42s
Clicking Snapshot now opens a named-prompt dialog (matching the app's
modal styling) and flushes the current step/guide first, so the
snapshot reflects unsaved edits and is easy to identify later.
2026-06-13 20:07:55 -05:00
Tyler 1776089c08 Remove the guide-link panel button; Save now syncs linked archives
Template tests / tests (push) Successful in 1m39s
Template tests / tests (pull_request) Successful in 1m39s
For linked guides, "Save now" now also writes back to the linked
.sfgz archive (reporting a lock conflict if another session holds it),
making the dedicated link/share button in the Guide panel redundant.
The toolbar Share button and More > Linked guide… menu still cover
one-off exports and archive management.
2026-06-13 19:50:22 -05:00
Tyler 31bdbea7b5 Replace the disabled "Local guide" label with a "Share as file" action
Template tests / tests (push) Successful in 1m39s
Template tests / tests (pull_request) Successful in 1m39s
The guide panel's link button was a dead disabled control for local
guides. It now shares the guide as a .sfgz archive instead, while
linked guides keep the existing archive-management dialog.
2026-06-13 19:38:05 -05:00