Commit Graph

1533 Commits

Author SHA1 Message Date
Luca Rossi
7dc8b317af Merge pull request #16 from refactoringhq/task/rimuovi-is-a
feat: Rimuovi proprietà 'Is a' — type: come chiave canonica
2026-02-23 20:56:18 +01:00
lucaronin
db5d5c98d5 fix: cargo fmt vault.rs formatting 2026-02-23 20:48:37 +01:00
lucaronin
bd58557a71 test: add tests for ReferencedByPanel and useReferencedBy hook
14 new tests covering:
- ReferencedByPanel component: empty state, grouping by viaKey,
  count badge, navigation, archived/trashed styling
- useReferencedBy integration: forward relationship resolution,
  grouped display, count, navigation, Type exclusion,
  alias resolution, self-reference prevention

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:48:26 +01:00
lucaronin
800a0df61b fix: cargo fmt vault.rs formatting 2026-02-23 20:48:22 +01:00
lucaronin
5b91b25713 feat: add Referenced By panel for bidirectional relationships
Computed reverse relationships — purely frontend, no Rust changes.
The useReferencedBy hook scans all entries' relationships maps to find
those referencing the current note via frontmatter wikilinks.

References are grouped by relationship key (e.g. "via Belongs to",
"via Related to") and displayed between Relationships and Backlinks.

Architecture decision: "Referenced by" is read-only. To remove a
reverse reference, navigate to the source note. Deleting a note
automatically removes it from all computed reverse references.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:47:05 +01:00
lucaronin
964daea45e design: wikilink-colorati wireframes
Two frames showing the colored wikilink feature:
1. Editor view with wikilinks colored per note type (red=Experiment,
   yellow=Person/Event, purple=Responsibility, green=Topic)
2. Broken link state with muted color + dashed underline + reduced opacity

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:46:21 +01:00
lucaronin
9cfd956940 design: Referenced By panel wireframes
Three frames showing the new bidirectional relationships UI:
1. Inspector with multiple references grouped by relationship type
2. Empty state (no references)
3. Many backlinks scenario with references from multiple relationship types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:45:31 +01:00
lucaronin
7140bf052d feat: color wikilinks by destination note type
Wikilinks in the editor now show the accent color of the target note's
type (Project=red, Person=yellow, Topic=green, etc.) instead of always
being blue. Broken links (target not found) show muted text with a
dashed underline.

- Extract wikilink color resolution to src/utils/wikilinkColors.ts
- Use getTypeColor from typeColors.ts (single source of truth)
- Support alias and pipe-syntax matching in findEntryByTarget
- Add wikilink--broken CSS class for non-existent targets
- Add 17 unit tests covering all color resolution cases
- Update mock data with wikilinks to various note types + broken link

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:44:30 +01:00
lucaronin
ca8637b155 fix: cargo fmt vault.rs formatting 2026-02-23 20:34:22 +01:00
lucaronin
4511951e81 design: inspector before/after wireframe for Is a removal
Shows the before state (with duplicate is_a property row highlighted in
orange) and the after state (clean inspector with only the Type row).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:34:22 +01:00
lucaronin
21a624b633 test: add migration and type-field parsing tests
Rust (9 new tests):
- test_parse_type_field: 'type:' in YAML maps to is_a field
- test_parse_type_field_takes_precedence_over_is_a: 'type' wins over 'Is A'
- test_parse_legacy_is_a_still_works: backward compat for 'Is A:'
- test_parse_snake_case_is_a_still_works: backward compat for 'is_a:'
- test_migrate_file_is_a_to_type: single-file migration
- test_migrate_file_quoted_is_a_to_type: handles "Is A" variant
- test_migrate_file_preserves_existing_type: type: takes precedence
- test_migrate_file_no_change_needed: skip already-migrated files
- test_migrate_vault: vault-wide migration counts correctly

Frontend (2 new tests):
- frontmatterToEntryPatch maps 'type' key to isA field
- DynamicPropertiesPanel skips is_a/Is A/type from property list

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:34:22 +01:00
lucaronin
c8e87f0ab9 feat: replace 'Is a' / 'is_a' with 'type' as the canonical frontmatter key
- Rust Frontmatter struct now parses both 'type' (new) and 'Is A'/'is_a' (legacy),
  with 'type' taking precedence via resolve_type()
- Added migrate_is_a_to_type() vault-wide migration (runs on startup, also exposed as Tauri command)
- Frontend: buildNoteContent and resolveNewType now emit 'type:' in YAML
- Inspector SKIP_KEYS hides 'is_a', 'Is A', 'type', 'title' from property list
  (TypeRow already renders the type with its dedicated UI)
- frontmatterToEntryPatch handles both 'type' and legacy 'is_a' keys
- Mock data updated: all YAML content uses 'type:' instead of 'is_a:'/'Is A:'
- Tests updated to expect 'type:' in generated frontmatter

Product decision: internal VaultEntry field stays as `isA` (TS) / `is_a` (Rust)
to minimize blast radius. The user-facing change is the YAML key and inspector.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:34:22 +01:00
lucaronin
9b26969d2f design: click-opens-new-tab wireframes
Two frames showing click vs Cmd+Click behavior:
1. Click — Replace Current Tab (note replaces active tab content)
2. Cmd+Click — Open New Tab (new tab created, original preserved)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:14:23 +01:00
lucaronin
4447a8a873 feat: click opens note in current tab, Cmd+Click opens new tab
Regular click on a note in NoteList now replaces the current tab content
instead of always creating a new tab. Cmd+Click (or Ctrl+Click) opens
in a new tab. If the note is already open in any tab, clicking just
switches to that tab regardless of modifier key.

- NoteItem: simplified to accept single onClickNote callback
- NoteList: routes click via metaKey/ctrlKey to onReplaceActiveTab or onSelectNote
- useTabManagement: handleReplaceActiveTab checks all tabs before replacing
- Extracted loadAndSetTab/isTabOpen/routeNoteClick helpers for code health

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:12:54 +01:00
lucaronin
d54c956082 docs: fix design file instructions — additive only, no cp ui-design.pen 2026-02-23 20:11:09 +01:00
Luca Rossi
e5612d933e Merge pull request #19 from refactoringhq/task/editor-save-bug
fix: remove broken auto-save, add explicit Cmd+S save, fix rename-before-save
2026-02-23 20:02:02 +01:00
lucaronin
93aa709217 merge: resolve .claude-done conflict 2026-02-23 20:01:51 +01:00
lucaronin
ffa93ea511 docs: add .claude-done completion summary
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 19:55:36 +01:00
lucaronin
e608d1f412 test: update E2E test for Cmd+S save + add design file
- E2E test now verifies Cmd+S shortcut shows a save toast
- Added design/editor-save-bug.pen wireframe

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 19:55:05 +01:00
lucaronin
785720b3dd fix: replace broken auto-save with explicit Cmd+S save
Removes the debounced auto-save (useAutoSave hook) which was causing the
editor to reload previous content. Replaces it with explicit save on
Cmd+S (⌘S), consistent with the git-based UX of the app.

- Removed useAutoSave hook and its debounce mechanism
- Added useSaveNote hook for direct persist-to-disk
- Added useEditorSave hook that manages pending content buffer + save
- Cmd+S now persists the current editor content immediately
- Rename-before-save: saves pending content before rename to prevent
  the "Failed to rename note" error
- Updated E2E test to verify Cmd+S behavior
- 471 tests passing, code health gates passed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 19:53:36 +01:00
lucaronin
36f4f5048a design: merge properties-inspector frames into ui-design.pen 2026-02-23 19:44:50 +01:00
Luca Rossi
e174c782cb Merge pull request #17 from refactoringhq/task/properties-inspector
feat: Properties inspector — editable vs read-only distinction
2026-02-23 19:42:03 +01:00
lucaronin
2720757620 docs: update abstractions for properties inspector Info section
Updated Inspector Abstraction docs to describe the new two-section
layout (editable properties + read-only Info section) and the
SKIP_KEYS filter. Updated .claude-done with task summary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 15:37:20 +01:00
lucaronin
23ca40c19a fix: hide is_a/Is A from editable properties (already shown as TypeRow)
The is_a frontmatter key was appearing both as the TypeRow badge and
as a regular editable property. Added both 'is_a' and 'Is A' variants
to SKIP_KEYS to prevent duplication (snake_case used in mock data,
space-separated used in real vault YAML).

Added test verifying is_a is skipped when TypeRow is present.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 15:35:50 +01:00
lucaronin
6527db10a2 feat: distinguish editable from read-only properties in inspector
Split the Properties panel into two visually distinct sections:

1. **Properties** (editable): frontmatter properties with interactive
   hover background, cursor pointer, and click-to-edit. Includes Type
   row, Status, Owner, tags, and all user-defined properties.

2. **Info** (read-only): derived metadata shown below a border
   separator with an "Info" header. Uses muted color (--text-muted)
   with no hover states or click interaction. Includes:
   - Modified date
   - Created date (new)
   - Word count
   - File size (new, formatted as B/KB/MB)

Product decisions:
- Info section uses --text-muted for both labels and values (more
  dimmed than the --muted-foreground used by editable labels)
- Editable rows get rounded hover:bg-muted on the full row
- Type row stays at top of Properties as an identity element
- Added Created date and file Size to give more useful metadata

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 15:19:57 +01:00
lucaronin
67b8d1830c design: properties-inspector wireframes
Two frames showing the visual distinction between editable properties
and read-only Info section:
- Frame 1: Full inspector with editable properties (hover states,
  cursor pointer) and Info section (muted, non-interactive)
- Frame 2: Side-by-side comparison of editable vs read-only styling

Product decisions:
- Separate "Info" section for derived metadata (Modified, Created,
  Words, Size)
- Info uses --text-muted color, no hover/click states
- Editable properties keep interactive styling (hover bg, pointer)
- Type row stays at top of Properties section as identity element

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 15:15:50 +01:00
Luca Rossi
9b44f5d564 Merge pull request #15 from refactoringhq/task/auto-build-release
feat: auto-build, GitHub Release, and in-app updater
2026-02-23 15:08:55 +01:00
lucaronin
0d4dba2161 merge: resolve conflicts with main — use self-hosted runner config 2026-02-23 15:02:34 +01:00
lucaronin
0053d3b985 fix: resolve TypeScript build errors and add E2E test
- Fix type annotations in App.tsx (setTabs callback)
- Cast restoreWikilinksInBlocks result in Editor.tsx for BlockNote compatibility
- Fix vi.fn() mock types in useAutoSave.test.ts
- Add E2E test verifying editor loads and renders note content
- Update .claude-done with editor-save-bug summary

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 15:01:48 +01:00
lucaronin
0e7865c667 fix: implement auto-save for editor content
The editor was not persisting changes to disk. Edits would be lost
when closing and reopening a note.

Changes:
- Add save_note_content Tauri command (Rust) with read-only file check
- Add save_note_content mock handler for browser testing
- Add useAutoSave hook with 500ms debounce and flush-on-tab-switch
- Wire up BlockNote onChange → markdown serialization → auto-save
- Add restoreWikilinksInBlocks utility (reverse of injectWikilinks)
  to convert wikilink nodes back to [[target]] before markdown export
- Extract shared walkBlocks helper to eliminate code duplication
- Suppress onChange during programmatic content swaps (tab switching)
- 12 new frontend tests + 5 new Rust tests (all 469 + 174 passing)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 14:48:21 +01:00
lucaronin
23c6a5d0d2 ci: optimize self-hosted build — incremental Rust, drop Swatinem cache 2026-02-23 14:32:29 +01:00
lucaronin
0fbebfbfaa ci: use self-hosted mac-mini runner for macOS build 2026-02-23 14:30:36 +01:00
lucaronin
e3a88db08a ci: build only for Apple Silicon (aarch64), drop x86_64 2026-02-23 14:21:57 +01:00
lucaronin
81f7b163e4 ci: retry after billing fix 2026-02-23 14:18:16 +01:00
lucaronin
ff9b98e14e ci: simplify release — drop lipo/re-signing, use per-arch dmg 2026-02-23 14:13:12 +01:00
lucaronin
1cccfd70cd ci: fix signing — hardcode empty password, update pubkey 2026-02-23 13:36:09 +01:00
lucaronin
b87ebe59a6 ci: regenerate Tauri signing keypair (no password) 2026-02-23 13:18:43 +01:00
lucaronin
369792f738 ci: fix signing key password secret name 2026-02-23 12:28:24 +01:00
lucaronin
8533984508 ci: fix signing key secret 2026-02-23 12:11:37 +01:00
lucaronin
731a2e5188 fix: set proper bundle identifier for release builds 2026-02-23 12:03:43 +01:00
lucaronin
66b9518613 fix: wire useViewMode into App and fix filterEntries arity 2026-02-23 12:01:46 +01:00
Luca Rossi
5ebcf41d47 feat: auto-build, GitHub Release, and in-app updater (#14)
* ci: auto-release workflow on merge to main

Rewrite .github/workflows/release.yml to trigger on every push to main
instead of manual tag pushes. The workflow now:

- Computes version as 0.YYYYMMDD.GITHUB_RUN_NUMBER
- Builds aarch64-apple-darwin and x86_64-apple-darwin in parallel
- Merges them into a universal binary using lipo
- Creates a universal .dmg and signed updater tarball
- Generates latest.json with per-arch and universal platform entries
- Publishes a GitHub Release with auto-generated release notes
- Updates a GitHub Pages release history site (gh-pages branch)

Product decisions:
- Universal binary approach: copy arm64 .app as base, lipo the main
  executable, keep everything else from arm64 (shared frameworks are
  architecture-independent). This is the standard Tauri pattern.
- Per-arch updater tarballs are also uploaded so the Tauri updater can
  download the correct arch-specific build (smaller download).
- Release notes are auto-generated from git log since last tag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ci: github pages with release history

Use peaceiris/actions-gh-pages@v4 to deploy a release history site.
The page fetches releases.json (also deployed) and renders each release
with date, notes, and download links for .dmg files.

This handles the gh-pages branch creation automatically on first run.
The page is available at https://refactoringhq.github.io/laputa-app/

Product decision: used fetch() to load releases.json at runtime instead
of inlining it, which is cleaner and avoids shell escaping issues with
release note content. The releases.json is deployed alongside index.html.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: in-app update notification UI

Replace the old window.confirm updater with a proper React-based
update notification system:

- useUpdater hook now exposes state machine (idle → available →
  downloading → ready) and actions (startDownload, openReleaseNotes,
  dismiss)
- UpdateBanner component renders at the top of the app shell:
  - "Available" state: shows version, Release Notes link, Update Now
    button, dismiss X
  - "Downloading" state: animated spinner, progress bar with percentage
  - "Ready" state: Restart Now button to apply the update
- Silently checks on startup after 3s delay; fails silently on
  network errors or 404
- Release Notes link opens the GitHub Pages release history site

Product decisions:
- Banner at top of app (not a modal) — non-intrusive, visible but
  not blocking. User can dismiss and continue working.
- Progress bar shows during download so user knows it's working.
- Separate "Restart Now" state after download so user controls when
  the app restarts (they may have unsaved work).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: updater component tests

Rewrite useUpdater hook tests and add UpdateBanner component tests:

Hook tests (10 cases):
- Starts in idle state
- Does nothing when not in Tauri
- Checks for updates after 3s delay
- Stays idle when no update available
- Transitions to available when update found
- Handles missing release body gracefully
- Stays idle on network error (fails silently)
- Dismiss returns to idle
- openReleaseNotes opens correct URL
- startDownload transitions through downloading to ready

Component tests (10 cases):
- Renders nothing when idle
- Renders nothing on error
- Shows version and buttons when available
- Update Now calls startDownload
- Release Notes calls openReleaseNotes
- Dismiss button works
- Shows progress bar during download
- Shows 0% at start of download
- Shows restart button when ready
- Restart button calls restartApp

All 457 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* design: auto-build-release wireframes

Copy ui-design.pen as base. Frames to be added for:

1. Update notification banner (visible state) — horizontal bar at
   top of app shell with version text, Release Notes link, Update
   Now button, and dismiss X
2. Update download progress state — spinner icon, progress bar with
   percentage, downloading text
3. "Restart to apply" state — green accent, version text, Restart
   Now button

Note: Pencil editor was not available during this session. The base
design file is committed; frames will be added when the editor is
accessible. The implemented component (UpdateBanner.tsx) serves as
the source of truth for the design.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: update ARCHITECTURE.md with release/update system

Add comprehensive documentation for:
- Release pipeline (4-phase workflow: version → build → release → pages)
- Versioning scheme (0.YYYYMMDD.RUN_NUMBER)
- Universal binary strategy (lipo merge)
- Updater endpoint and latest.json manifest
- In-app update UI state machine
- GitHub Pages release history site

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: rustfmt formatting

* fix: rustfmt build.rs

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:50:36 +00:00
lucaronin
197a1b2428 fix: rustfmt build.rs 2026-02-23 11:40:33 +01:00
lucaronin
001f2e728e fix: rustfmt formatting 2026-02-23 11:28:11 +01:00
lucaronin
624271ffab docs: update ARCHITECTURE.md with release/update system
Add comprehensive documentation for:
- Release pipeline (4-phase workflow: version → build → release → pages)
- Versioning scheme (0.YYYYMMDD.RUN_NUMBER)
- Universal binary strategy (lipo merge)
- Updater endpoint and latest.json manifest
- In-app update UI state machine
- GitHub Pages release history site

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:47:13 +01:00
lucaronin
e4c0fe6b36 design: auto-build-release wireframes
Copy ui-design.pen as base. Frames to be added for:

1. Update notification banner (visible state) — horizontal bar at
   top of app shell with version text, Release Notes link, Update
   Now button, and dismiss X
2. Update download progress state — spinner icon, progress bar with
   percentage, downloading text
3. "Restart to apply" state — green accent, version text, Restart
   Now button

Note: Pencil editor was not available during this session. The base
design file is committed; frames will be added when the editor is
accessible. The implemented component (UpdateBanner.tsx) serves as
the source of truth for the design.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:46:27 +01:00
lucaronin
362f6c67a5 test: updater component tests
Rewrite useUpdater hook tests and add UpdateBanner component tests:

Hook tests (10 cases):
- Starts in idle state
- Does nothing when not in Tauri
- Checks for updates after 3s delay
- Stays idle when no update available
- Transitions to available when update found
- Handles missing release body gracefully
- Stays idle on network error (fails silently)
- Dismiss returns to idle
- openReleaseNotes opens correct URL
- startDownload transitions through downloading to ready

Component tests (10 cases):
- Renders nothing when idle
- Renders nothing on error
- Shows version and buttons when available
- Update Now calls startDownload
- Release Notes calls openReleaseNotes
- Dismiss button works
- Shows progress bar during download
- Shows 0% at start of download
- Shows restart button when ready
- Restart button calls restartApp

All 457 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:45:40 +01:00
lucaronin
649d1ce2e0 feat: in-app update notification UI
Replace the old window.confirm updater with a proper React-based
update notification system:

- useUpdater hook now exposes state machine (idle → available →
  downloading → ready) and actions (startDownload, openReleaseNotes,
  dismiss)
- UpdateBanner component renders at the top of the app shell:
  - "Available" state: shows version, Release Notes link, Update Now
    button, dismiss X
  - "Downloading" state: animated spinner, progress bar with percentage
  - "Ready" state: Restart Now button to apply the update
- Silently checks on startup after 3s delay; fails silently on
  network errors or 404
- Release Notes link opens the GitHub Pages release history site

Product decisions:
- Banner at top of app (not a modal) — non-intrusive, visible but
  not blocking. User can dismiss and continue working.
- Progress bar shows during download so user knows it's working.
- Separate "Restart Now" state after download so user controls when
  the app restarts (they may have unsaved work).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:44:31 +01:00
lucaronin
5268168abb ci: github pages with release history
Use peaceiris/actions-gh-pages@v4 to deploy a release history site.
The page fetches releases.json (also deployed) and renders each release
with date, notes, and download links for .dmg files.

This handles the gh-pages branch creation automatically on first run.
The page is available at https://refactoringhq.github.io/laputa-app/

Product decision: used fetch() to load releases.json at runtime instead
of inlining it, which is cleaner and avoids shell escaping issues with
release note content. The releases.json is deployed alongside index.html.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:42:15 +01:00
lucaronin
cae6fced35 ci: auto-release workflow on merge to main
Rewrite .github/workflows/release.yml to trigger on every push to main
instead of manual tag pushes. The workflow now:

- Computes version as 0.YYYYMMDD.GITHUB_RUN_NUMBER
- Builds aarch64-apple-darwin and x86_64-apple-darwin in parallel
- Merges them into a universal binary using lipo
- Creates a universal .dmg and signed updater tarball
- Generates latest.json with per-arch and universal platform entries
- Publishes a GitHub Release with auto-generated release notes
- Updates a GitHub Pages release history site (gh-pages branch)

Product decisions:
- Universal binary approach: copy arm64 .app as base, lipo the main
  executable, keep everything else from arm64 (shared frameworks are
  architecture-independent). This is the standard Tauri pattern.
- Per-arch updater tarballs are also uploaded so the Tauri updater can
  download the correct arch-specific build (smaller download).
- Release notes are auto-generated from git log since last tag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:41:20 +01:00