Commit Graph

44 Commits (master)

Author SHA1 Message Date
mirivlad 23b3d07071 fix: tree DnD — correct cycle detection, reactive indicators, canonical reload
Backend:
- Fix MoveNode validation: wouldCreateCycle walks from newParentID up
  toward root, rejects if nodeID is encountered (parent into descendant)
- Allow moving descendant to ancestor (C into A) and child to root
- Add isContainerType validation for new parent
- Add 8 tests covering all scenarios + duplicate ID invariant

Frontend TreeNode.svelte:
- Build parent map from full tree (not just loaded children)
- canDrop uses parent map for cycle detection
- Reactive drop-valid/drop-invalid CSS via pre-computed dropAllowed map
- Keyed {#each nodes as node (node.id)} for correct identity tracking
- Auto-expand container on 600ms drag-over hover
- Proper dragleave detection (ignore transitions to child elements)
- Clean up state on dragend

Frontend App.svelte:
- reloadTreePreservingExpanded: fresh roots + children (no patching)
- Root area visual drop indicator (dashed outline)
- dragleave handler for root area

Clean up stale GUI dist assets
2026-06-03 05:27:20 +08:00
mirivlad b6a3a2238d fix: tab highlight reactivity, cleanup docs and build scripts
- Fix tab highlight not updating visually — switch from class={tabClass()}
  to Svelte's class:active directive for proper reactive class binding
- Rewrite README.md with full project structure, architecture, build guide
- Rewrite build.sh to build both GUI and server, output to build/
- Add scripts/build.sh for granular builds (gui/server/all)
- Add build/, frontend-dist/, and test vault dirs to .gitignore
- Remove stale binaries from project root
- Update AGENTS.md session summary
2026-06-03 05:08:58 +08:00
mirivlad 105657400b fix: context menu close, OpenFolder for TypeFile nodes, tab highlight visibility
- FileTreeRow handleShowInFolder now closes the menu (menuOpen = false)
- OpenFolder: TypeFile nodes with empty FsPath fall back to file record path
- Active tab background increased to rgba(99,102,241,0.12), weight 600, border #818cf8
2026-06-03 04:56:11 +08:00
mirivlad cc3500c14f fix: sidebar refresh, context menu position, show-in-explorer for all items
Sidebar refresh:
- addFile/addFolder now use currentFolderId || selectedNode.id as parent
- startImport stores pendingImportParent; confirmImport uses it
- setNodeChildren also updates node.has_children for toggle arrow reactivity
- navigateToFolder expands the folder node in sidebar tree

Context menu position:
- FileTreeRow stores menuX/menuY from contextmenu event coords
- Menu uses position:fixed with cursor-relative left/top and window clamp

Show in explorer:
- Sidebar context menu uses file.showInExplorer label
- en.js: add file.showInExplorer key
2026-06-03 04:46:42 +08:00
mirivlad 3c9b9edf8c fix: dynamic sidebar tree refresh after import
- reloadTreePreservingExpanded no longer replaces the whole tree,
  only patches children of expanded nodes in-place
- New refreshParentNode(nodeId) function updates a single parent's children
- createFile, duplicateItem, confirmImport use refreshParentNode instead of reloadTreePreservingExpanded
- No intermediate render where children are lost
2026-06-03 04:34:27 +08:00
mirivlad 81405ed61b fix: refresh sidebar tree after import/create/duplicate in files tab
- Call reloadTreePreservingExpanded after createFile, confirmImport, duplicateItem
- New folders created inside a case now appear in sidebar without restart
- Add AGENTS.md with build instructions
2026-06-03 04:28:41 +08:00
mirivlad baf57e993d feat: move-to-root, active tab highlight, show-in-explorer for all file items
- Add 'Move to root' context menu item for non-root sidebar tree nodes
- Improve active tab visual contrast (background + font-weight)
- FileTreeRow: show-in-explorer inline button & context menu for all items
- OpenFolder now resolves file-type nodes to parent directory
- handleShowInFolder uses item.nodeId || item.id for FileDTO compatibility
- Add i18n key nav.moveToRoot (ru/en)
2026-06-03 04:01:55 +08:00
mirivlad c941f05dab gui: sidebar tree UX fixes — has_children, preserve expanded, double-click, DnD visual
- Backend: add HasChildren field to NodeDTO; ListWorkspaceTree/ListChildren
  populate it by querying CountChildren for container types
- Node repository: add CountChildren(parentID, types...) method
- TreeNode: toggle shown only when has_children (not isContainer); double-click
  on row = expand/collapse; icon click = expand/collapse; drop-valid class via
  DOM classList for DnD highlight; auto-expand collapsed container on 500ms
  hover during drag; auto-scroll near edge during drag
- App.svelte: selectNode no longer resets workspaceTree/expanded; new
  reloadTreePreservingExpanded() helper re-fetches children for all expanded
  nodes after DnD move / delete; deleteWorkspaceNode preserves expanded state
2026-06-03 03:48:53 +08:00
mirivlad 9260582072 gui: sidebar tree model fix — only container nodes, improved DnD + context menu
- Backend: ListWorkspaceTree/ListWorkspaceChildren filter to container types
  only (case, client, project, folder, document, recipe)
- TreeNode: full-row context menu (removed label stopPropagation),
  double-click toggles expand, icon-click toggles expand, DnD auto-expand
  on 500ms hover, auto-scroll near edges, drag-over highlight via classList
- App.svelte: toggleExpand uses ListWorkspaceChildren, submitCreateNode uses
  ListWorkspaceChildren for child tree population
- Note/file nodes no longer appear in the sidebar workspace tree
2026-06-03 03:33:13 +08:00
mirivlad b2dcb116c9 gui: drag-and-drop sidebar, tree expand, localization fixes
TreeNode.svelte:
- Native HTML5 drag-and-drop with move effect
- Lazy tree expand/collapse (arrow for container types only)
- Drop validation: no self-drop, container-only, descendant check
- 'case' icon kind added

App.svelte:
- toggleExpand loads children via ListChildren into tree
- handleNodeDrop calls MoveNode(draggedId, targetId), refreshes tree
- Root workspace area is a drop target (handleDropRoot)
- Overview section shows nodeKindLabel instead of raw type enum
- Context menu shows Create only for container types
- Create modal title uses 'Создать элемент'
- submitCreateNode expands parent after child creation

TemplateIcon.svelte: added 'case' icon (folder-like with dividers)
i18n: added nav.createNode key (ru+en)
2026-06-03 03:18:04 +08:00
mirivlad f022f46909 gui: fix sidebar icons, create modal, and type display
- TreeNode.svelte: no white spacer for leaf nodes, iconKind maps node.type
  directly, 32px rows with hover/selected states
- App.svelte: header shows localized nodeKindLabel, auto-select created
  node, create modal with Пустое дело card + template descriptions,
  disable button until name+type chosen
- i18n: add kind.folder/note/file, template descriptions,
  template.optionNone → Пустое дело / Empty case
2026-06-03 02:58:27 +08:00
mirivlad a6b0f9d7e6 Rebuild GUI binary with updated frontend assets 2026-06-03 02:40:43 +08:00
mirivlad d285f9ad8b sync_apply FS-first rewrite; CreateNodeFromTemplate rollback; DeleteNodeAndChildren fail on trash errors; PLAN.md update
- applyRemoteNodeUpdate: FS-first with SafeVaultPath validation, must-fail os.Rename
- applyRemoteNodeMove: FS-first for folders and notes/files
- moveNodeFiles: rewritten FS-first with atomic DB transaction
- applyRemoteNoteMove: delegates to moveNodeFiles
- CreateNodeFromTemplate: rollbackChildren on any child creation failure
- DeleteToTrash: skip rename if source file already missing
- DeleteNodeAndChildren: fail on deleteFileRecords errors and trash move failures
- docs/PLAN.md: update step 14 status with known gaps
2026-06-03 02:22:49 +08:00
mirivlad 7e38ffed7b bindings_nodes: fix parent variable redeclaration (rename to parentVal) 2026-06-03 02:18:10 +08:00
mirivlad a31f5fd702 fix: third stabilization pass — template children as nodes, atomicity, fs_path validation, sync_apply compat, smoke test 2026-06-03 02:05:53 +08:00
mirivlad 49c0fda61c chore: add wails.json, remove wails3 artifacts, rebuild binaries
- Add wails.json for Wails v2 build
- Remove wails3 boilerplate (build/Taskfile.yml, build/config.yml, etc.)
- Add server-data/ to .gitignore
- Rebuild frontend-dist and GUI binary
2026-06-03 01:48:12 +08:00
mirivlad 7b2a1da529 fix: note/file move ops, rename/move atomicity, importDir folder creation
- importDir: create physical folder for imported directories
- RenameNode/MoveNode: os.Rename before DB updates (atomicity)
- RenameNode note/file: fail if physical file missing
- MoveNode: file renames before DB updates
- applyRemoteNoteOp: handle OpMove for notes/files
- applyRemoteNodeMove: handle notes/files with empty FsPath
- MoveNode sync payload: no fs_path for notes/files
- Add 7 tests covering all fixes
2026-06-03 01:32:47 +08:00
mirivlad 20a05569ac fix: второй стабилизационный pass vault layout — sync payload, bindings, vaultPath, tests
sync_apply.go:
- applyRemoteNodeCreate: полный payload (template_id/fs_path/sort_order/archived),
  INSERT сохраняет все поля, для folder-like создаётся физическая папка.
- applyRemoteNodeUpdate: принимает fs_path/template_id/archived,
  физическое переименование папки при изменении title/fs_path.
- applyRemoteNodeMove: принимает fs_path, обновляет parent_id+fs_path,
  физически перемещает папку (folder-like) или file record (note/file).

bindings_nodes.go:
- MoveNode: node.FsPath = newFsPath после UpdateFsPath;
  sync.RecordOp отправляет новый fs_path; note/file move to root — файл в vault root.
- RenameNode: EntityFile для file, EntityNote для note;
  коллизия → генерация уникального имени; файл переименовывается только после os.Rename.
- DeleteNode: единый вызов a.files.DeleteNodeAndChildren(), дублирование удалено.
- Исправлен deadlock с SetMaxOpenConns(1) — Query/Exec больше не конфликтуют.

files.Service.vaultPath: filepath.Rel-based проверка,
  sibling-prefix escape (/tmp/vault vs /tmp/vault_evil) отклоняется.

VaultCheck: SQL JOIN с n.deleted_at IS NULL, чтобы удалённые узлы
  не показывались как missing files.

Добавлены тесты: RenameFileNodeUsesEntityFile, MoveNoteToRoot,
  DeleteFolderLeavesVaultCheckHealthy, SyncNodeCreatePreservesFields,
  VaultPathSiblingPrefixEscape.
2026-06-02 17:03:05 +08:00
mirivlad 66c5c81f39 fix: стабилизация vault layout — rename/move/delete note/file vs folder разведены, sync apply без spaces/, VaultCheck усилен
- RenameNode/DeleteNode/MoveNode: note/file и folder-like nodes
  обрабатываются по-разному (file record vs физическая папка)
- DeleteNode: рекурсивный soft-delete всех descendants
- SafeVaultPath возвращает clean relative, filepath.Join в sync_apply.go
- Fallback spaces/ → .verstak/remote-inbox в applyRemoteNoteCreate
- VaultCheck: проверка parent_id != nil, orphan descendants,
  fs_path folder на диске
2026-06-02 16:36:43 +08:00
mirivlad 4f01f2de2e fix: complete vault layout transition — fs_path everywhere, no more spaces/
- notes.Create(): .md files stored in parent node's fs_path folder
- files.CopyIntoVault/CreateEmptyFile/Duplicate: use parent fs_path
- files.AddPathCopy/AddPathLink: use parent fs_path, set folder fs_path
- files.DeleteNodeAndChildren: move physical folder to .verstak/trash
- UpdateFsPathRecursive: use SafeDisplayNameToPathSegment(child.Title)
- sync_apply.go note ops: use fs_path instead of spaces/
- internal/gui/server.go file upload: use n.FsPath instead of nodeSlug
- VaultCheck diagnostic: walk nodes/files, verify paths on disk
- Tests: create/rename/move/delete/name-conflict/vault-check all pass
2026-06-02 15:43:40 +08:00
mirivlad 0b26f7e5b3 refactor: implement template-driven node tree and human-readable vault layout
Unified Node model: added template_id, fs_path, archived, sort_order fields.
Template registry: system templates embedded as JSON (folder/project/client/
document/recipe), with Registry for enabled/disabled/filtered access.
SafeDisplayNameToPathSegment: human-readable path segments with Cyrillic
support, illegal char replacement, uniqueness via numeric suffixes.
Sidebar refactored: system views (Today/Inbox/Activity) separate from
workspace tree. Creation menu built dynamically from enabled templates.
Create/Rename/Move: physical folder operations with fs_path update,
recursive descendant path updates.
DB migration 012: adds template_id, fs_path, archived columns.
Vault migration command: rebuilds fs_path for existing nodes.
Tests: safename, registry, node model, repository integration.
Docs: VAULT_LAYOUT.md, TEMPLATES.md, PLAN.md updated.
i18n: nav.system, nav.workspace, template.*, common.rename/archive,
migrate.* keys added to ru.json and en.json.
2026-06-02 12:47:06 +08:00
mirivlad 12f2916a24 followup: SafeVaultPath in note update, email i18n, strict check-i18n.sh
- applyRemoteNoteUpdate: use SafeVaultPath for vault mode, skip non-vault with log
- Email subjects/bodies moved to Go i18n (confirm + reset) in ru.json and en.json
- check-i18n.sh: ru/en key mismatch now FAIL (not WARNING)
- All builds, tests, frontend pass
2026-06-02 11:40:27 +08:00
mirivlad 7091397649 server i18n: move inline HTML to templates.go, localize all handler strings
- Admin & user dashboard HTML moved from handlers to templates.go with i18n.T()
- SafeVaultPath applied in sync_apply.go (note/file create/update, blob restore)
- DeleteNode/RenameNode/MoveNode fixed: correct activity type / entity variant
- Added TypeNoteDeleted, TypeNodeDeleted, TypeFolderMoved activity constants
- Added locale() helper on Server struct, removed hardcoded 'ru' in handlers
- Password policy loosened: 8-256 chars, any characters, machine-readable error codes
- check-i18n.sh: Go Cyrillic = FAIL with explicit exception list, Go locale key consistency check
2026-06-02 11:26:54 +08:00
mirivlad 3089d777a8 refactor(gui): разделить app.go на binding-файлы по доменам, вынести sync apply
- app.go (1810→280 строк): только App struct, startup, DTOs, helpers
- bindings_{nodes,files,notes,actions,worklog,activity,sync,settings}.go
- sync_apply.go: все applyRemote* методы
- i18n: internal/i18n (Go, embed JSON) + frontend/src/lib/i18n (JS)
- core/sync/safe_path.go: SafeVaultPath
- scripts/check-i18n.sh: проверка хардкода кириллицы и bidi-символов
- build.sh: NVM loading, set -e

Все сборки (CLI, server, gui, frontend), go vet, go test проходят.
2026-06-02 10:47:38 +08:00
mirivlad 4a96aa3468 fix(sync): expand payloads, implement ApplyRemoteOp, fix SyncTestConnection and auto sync
- Expand all RecordOp payloads with full entity data needed for reconstruction
- Add applyRemoteOp with handlers for node/note/file/action/worklog CRUD
- SyncNow() now applies pulled ops to real DB tables + sets LastSeenServerSeq
- SyncTestConnection uses /api/auth/test instead of PairDevice
- autoSyncLoop respects configured interval with lastSync timing
- Add helper functions: nodePayload, notePayload, filePayload, actionPayload, worklogPayload
2026-06-02 08:02:15 +08:00
mirivlad 87c8dfcbea sync: overhaul sync system — device pairing, server_sequence, auto-sync, dashboards
BREAKING: replace legacy API keys with device tokens via pairing flow.
- Server: /api/client/pair, revoke, me endpoints; server_sequence + tombstones + idempotency
- Desktop client: PairDevice, GetMe, RevokeCurrent; auto-sync loop every 60s
- Config: device_token stored in separate file (0600), not config.yml
- Client DB: last_pull_seq migration for incremental pull
- Frontend (Svelte): settings modal with connect/disconnect/interval
- User dashboard (/dashboard): device list with status, revoke with password
- Admin dashboard (/admin/dashboard): devices table from /admin/api/devices
- CLI (cmd/verstak): updated for ServerSequence/GetState changes
- Fix: autoSyncLoop falls back to SQLite sync_state for server URL
- Fix: SyncSetInterval preserves server_url/device_id from SQLite
2026-06-02 02:26:05 +08:00
mirivlad b0d992b0d6 fix: rebuild GUI with login/password sync fields, make sync buttons more visible 2026-06-02 00:37:31 +08:00
mirivlad f8dc436709 feat: client auth — login/password flow, auto device reg, sync interval + improved sync UI 2026-06-01 23:36:19 +08:00
mirivlad 5b2cec5bcc sync: fix SyncStatus binding — remove invalid type assertion, use config for device ID 2026-06-01 22:55:50 +08:00
mirivlad 1a20edac44 feat: sync — client ops recording in core services
- internal/core/sync/: Service, Client, Blob packages

- RecordOp creates sync_ops entries for all mutations

- Client for push/pull/blob HTTP to server

- Blob SHA-256 hashing and local storage

- Wired into app.go alongside activity recording

- Device ID from config or fallback
2026-06-01 22:54:23 +08:00
mirivlad 996322f3a9 gui: actions CRUD + FromTemplate bindings + UI
- CreateAction / DeleteAction Wails bindings

- FromTemplate / ListTemplates bindings with recursive tree creation

- Plugin manager stored in App struct for template access

- Action creation modal (title, kind, data) in Overview and Actions tabs

- Delete action button on action cards

- Template selector in new-node dialog
2026-06-01 22:17:18 +08:00
mirivlad a098cf721c add missing Wails bindings for ListActivityFeed, ListActivityByNode, CountActivityByNode 2026-06-01 22:00:58 +08:00
mirivlad 3672e3133b activity: global feed, per-case log, sidebar section, today UX
- migration 009: target_type, target_id, target_path columns
- new Event fields: TargetType, TargetID, TargetPath
- ListActivityFeed (paginated global), ListActivityByNode (per-case)
- all Record() callsites pass target info
- frontend: Активность sidebar section with chronological feed
- per-case Активность tab with real data (was placeholder)
- today events: clickable, target-type badges, event counts
2026-06-01 02:53:56 +08:00
mirivlad 5a1c4c6d7f simplify ListTodayView: remove fallback table queries, pure activity_events + ListTodayNodes
TodayView now uses only activity_events (source of truth) + ListTodayNodes (ensure changed cases appear). Removed direct queries to nodes (notes) and files tables — those will come from activity_events going forward.
2026-06-01 02:45:55 +08:00
mirivlad c74fa3ad43 today dashboard: activity_events, ListTodayView with events timeline, frontend TodayDashboard separated from sidebar 2026-06-01 02:16:13 +08:00
mirivlad 69891e395c Today view and Inbox section: dynamic query, not stored sections
- ListTodayView() on backend: queries nodes created or updated today
  using local timezone day boundaries
- todayBoundaries() helper returns start/end of current day in RFC3339
- Section validation: Create() rejects today and inbox as node sections
- validSections moved from repository.go to types.go with IsValidSection
  and IsServiceSection helpers
- Frontend selectSection('today') calls ListTodayView instead of
  ListNodesBySection('today')
- FAB (create node) hidden in today and inbox sections
- CreateNode in app.go guarded against today/inbox sections
- types.go: today/inbox defined as service sections (sidebar only)
2026-06-01 01:39:02 +08:00
mirivlad a4ae22c445 File manager: selection, keyboard shortcuts, rename modal, security, localization
- Selection model: click=select, dblclick=open, Ctrl+click toggle,
  Shift+click range select, Ctrl+A, Esc to clear
- Keyboard shortcuts: Enter, Ctrl+Enter, F2, Backspace (navigate up),
  Delete/Backspace (delete with confirm)
- Rename modal replaces prompt() with validation via backend ValidateName
- Context menu: Open, Open External, Show in Folder, Rename, Duplicate,
  Copy, Cut, Delete — all with SVG icons and Russian labels
- Backend security: vaultPath/absPathSafe helpers prevent path traversal,
  validateName rejects .. / \ null bytes empty overlong names
- MoveNode auto-renames on name conflict (copy style)
- Duplicate uses (copy) (copy 2) suffix pattern
- Russian localization: all file type labels, preview messages, tooltips
- FilePreviewModal: fixed broken {/if} tag
2026-06-01 01:35:45 +08:00
mirivlad 2487d3bbaa Files tab: multi-selection, drag-and-drop, keyboard shortcuts, custom confirm modal, SVG icons 2026-06-01 01:16:51 +08:00
mirivlad 645d8878cc gui: complete Wails v2 vertical MVP — fixes, search, polished UI
Backend fixes:
- Wire search service in App struct, implement Search() bindings
- Fix OpenFile to use files.Service.Open() instead of stub
- Fix OpenFolder to open spaces/<slug>/ instead of vault root
- Remove unused imports and dead code in app.go

Frontend fixes:
- Add missing Svelte plugin to vite.config.js (blocking build error)
- Fix optional catch binding for compatibility
- Fix select dropdown rendering on Linux (appearance: none + custom arrow)
- Switch api/verstak.js to use generated Wails v2 bindings
- Include hand-written wailsjs bindings in repository
- Add build.sh to repository

Build:
  cd frontend && npm run build
  rm -rf cmd/verstak-gui/frontend-dist && cp -r frontend/dist cmd/verstak-gui/frontend-dist
  go build -tags 'gui production webkit2_41' -o verstak-gui ./cmd/verstak-gui
2026-05-31 23:48:38 +08:00
mirivlad b4010a5a24 gui: Wails v2 vertical MVP — bindings + UI
Go bindings (cmd/verstak-gui/app.go):
- Wire notes.Service, files.Service, actions.Service, worklog.Service
- CreateNote, ListNotes, ReadNote, SaveNote
- ListFiles, ListActions, RunAction
- ListWorklog, CreateWorklog
- Fix DTO mappings for core types

Frontend (frontend/src/):
- api/verstak.js: Wails v2 API wrapper (window.go.main.App)
- App.svelte: full rewrite with sidebar + 6 tabs + notes editor
- Section filtering → nodes by section
- Notes: create, open textarea editor, save, dirty tracking
- Quick actions, worklog entries, empty states
- Node creation modal with section select

Build:
  cd frontend && npm run build
  rm -rf cmd/verstak-gui/frontend-dist && cp -r frontend/dist cmd/verstak-gui/frontend-dist
  go build -tags 'gui production webkit2_41' -o verstak-gui ./cmd/verstak-gui
  ./verstak-gui
2026-05-31 20:37:46 +08:00
mirivlad ee503c338f gui: fix layout — full viewport, dark theme, sidebar+main
Problem: UI appeared as narrow dark panel on white background with scrollbar.

Root causes:
- html/body had no reset — default browser margin/padding = white borders
- index.html referenced non-existent /style.css
- .app used height:100vh but no width:100vw or overflow:hidden
- sidebar used fixed width instead of flexbox

Fixed:
- index.html: added critical CSS reset (html/body/#app = 100%, margin:0, overflow:hidden, dark bg)
- index.html: removed /style.css ref, changed lang to ru, title to Верстак
- App.svelte: complete layout rewrite
  - .app = flex, 100vw x 100vh, overflow:hidden
  - sidebar = 260px width, full height, flex column
  - main = flex:1, full height, flex column (header + content)
  - sidebar nav with sections + nodes using <button> elements
  - content area fills remaining space
  - proper dark theme colors throughout
- Rebuilt frontend/dist and cmd/verstak-gui/frontend-dist
2026-05-31 19:54:07 +08:00
mirivlad 77a7918569 gui: port frontend to Wails v2
Frontend build failure root cause:
- Vite 8 uses rolldown with Wails v3 typed-events plugin
- @wailsio/runtime (Wails v3) in frontend dependencies
- vite.config.js had wails('./bindings') plugin from Wails v3 template
- main.js used Svelte 5 mount() API but Svelte 4 required

Fixes:
- Remove @wailsio/runtime dependency
- Remove wails('./bindings') plugin from vite.config.js
- Replace Vite 8 with Vite 5.4.21 + Rollup (stable)
- Downgrade Svelte 5 to Svelte 4.2.19
- Downgrade @sveltejs/vite-plugin-svelte to v3.1.2
- Fix main.js: mount() -> new App({ target })
- Rewrite App.svelte with Wails v2 binding calls (window.go.main.App.*)
- UI: sidebar with sections, nodes, basic navigation

Build: cd frontend && npm run build -> dist/ (476ms)
Build GUI: go build -tags 'gui production webkit2_41' -o verstak-gui ./cmd/verstak-gui
Run: ./verstak-gui (window opens, no SIGSEGV)
2026-05-31 19:39:49 +08:00
mirivlad 600b67bc1e gui: add Wails v3 desktop app skeleton
- Go upgraded to 1.25.10 (required by Wails v3)
- Wails v3 installed (alpha.96)
- Frontend: Svelte+Vite scaffold in frontend/
- guimain.go: Wails GUI entry point (compiled with -tags gui)
- wails_service.go: stub service for Wails bindings
- Verstak desktop binary builds: go build -tags gui -o verstak-gui .
- CLI (./cmd/verstak/) unaffected
- Legacy HTTP GUI (internal/gui/) preserved as prototype
- Build: 24MB ELF binary with GTK4/WebKit2GTK-6

Build commands:
  CLI:  go build -o verstak ./cmd/verstak/
  GUI:  cd frontend && npm run build && go build -tags gui -o verstak-gui .
2026-05-31 15:45:52 +08:00
mirivlad 39271fc28f steps 4-6 + doc overhaul: files, notes, GUI, plugins docs
DOCUMENTATION (shift from personal to universal product):
- README.md: rewritten with 'one product, different doors' framing,
  universal entities table, audience segments
- 01_Product_Spec.md: removed personal references (sshkeeper, Godot,
  DokuWiki, servers), added audience segments (freelancer, repairmaster,
  developer, maker, consultant), universal scenarios
- 02_Architecture.md: added 'Plugins (Extensibility)' section with
  calendar/kanban/importer/template examples
- 03_Data_Model_Storage.md: added section 6 on plugin extensibility
  (node_meta, type registry, SQL migrations per plugin)
- 09_Extensibility.md (NEW): full plugin architecture — Lua runtime,
  plugin.json, hooks, sandbox, templates, registry
- PLAN.md: added step 16 (plugins), updated status table
- 00_README.md: rewritten product index with plugin principle

CODE — STEP 4 (Files):
- migration 002: files table (id, node_id, filename, path,
  storage_mode, size, sha256, mime, ...)
- FileService: AddExternal, CopyIntoVault, Get, ListByNode,
  MarkMissing, DeleteToTrash, Open (xdg-open)
- file_test.go: 5 tests (external, copy-vault, list-node,
  delete-trash, MIME guess)

CODE — STEP 5 (Notes):
- migration 003: notes table (node_id PK, file_id, format,
  original_format, encrypted)
- NoteService: Create (node+file+link), Read, Save (with backup to
  .verstak/history/), Delete, Load
- note_test.go: 3 tests (create-read, save-backup, delete)

CODE — STEP 6 (GUI):
- cmd/verstak-gui/main.go: launches GUI server, opens browser
- internal/gui/server.go: HTTP API for nodes/notes/files/search
- internal/gui/index.html.go: full inline SPA frontend (dark theme,
  sidebar tree, cards grid, note editor, search, create modals)
- Navigation: sidebar tree → click node → detail view with
  children + files cards → tab switch (overview/notes/files)
  → create node/note via modal → edit note in fullscreen
  textarea → save (with history backup)

Acceptance: go build ./... pass, go build -tags gui ./cmd/verstak-gui pass,
  go test ./... pass (20+ tests). GUI serves on random port, opens browser.
  API returns JSON for all resource types.
2026-05-30 20:35:04 +08:00