fix: FilesTab empty state, button styles, text preview, menu positioning
- Add reactive $: block in FilesTab to auto-load files on selectedNode change - Copy .btn/.btn-primary/.btn-sm styles into NotesTab.svelte and FilesTab.svelte (scoped CSS) - Fix FilePreviewModal: render .md with MarkdownPreview, text files in <pre><code>, external open in footer - Expand codeNames/textMimes in fileUtils.js to cover all text file types - Add fallback text read for unknown file types in _openPreview - Fix toggleMenu in FileTreeRow: position menu near button using getBoundingClientRect - Add debug logs for file open flow and menu positioning - Add tabindex+keydown to MarkdownPreview div (a11y partial fix) Co-Authored-By: OWL (Hermes Agent) <hermes@nousresearch.com>
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
- **Framework**: Svelte 4 (runes-free, compiler-only)
|
||||
- **Build tool**: Vite 5
|
||||
- **GUI shell**: Wails v2 (Go backend + WebKit frontend)
|
||||
- **GUI shell**: Wails v2 (Go backend + WebKit GTK frontend)
|
||||
- **Language**: Plain JavaScript (no TypeScript in Svelte files — `lang="ts"` is NOT used)
|
||||
|
||||
## Source Structure
|
||||
@@ -37,7 +37,11 @@ frontend/src/
|
||||
markdown/ # Markdown rendering + internal links
|
||||
util/ # Keyboard layout helper
|
||||
components/
|
||||
OverviewTab.svelte # Overview tab: meta, quick actions, recent items
|
||||
files/
|
||||
FilesTab.svelte # Files tab: file browser, preview, import, rename
|
||||
notes/
|
||||
NotesTab.svelte # Notes tab: note list, create form
|
||||
MarkdownEditor.svelte # Markdown textarea with toolbar
|
||||
MarkdownPreview.svelte # Rendered markdown preview
|
||||
NoteEditorPanel.svelte # Editor + preview layout, public API: insertText()
|
||||
@@ -51,13 +55,59 @@ frontend/src/
|
||||
|
||||
1. **Global UI state**: sidebar (system views, workspace tree), active tab, selected node/section
|
||||
2. **Lifecycle**: startup checks (GetStartupStatus, VerstakVersion, ListWorkspaceTree), event listeners
|
||||
3. **Top-level modals**: confirm, rename, import, worklog, inbox assign, link edit, settings, file preview
|
||||
3. **Top-level modals**: confirm, node rename, import (removed — now in FilesTab), worklog, inbox assign, link edit, settings, trash preview
|
||||
4. **Cross-cutting concerns**: navigation history (goBack, rememberNavigation), keyboard shortcuts, drag-and-drop orchestration, capture/inbox flow
|
||||
5. **Note editor lifecycle**: `noteEditor` state, `doOpenNote()`, `saveCurrentNote()`, `closeNoteEditor()`, link modal, internal link picker
|
||||
|
||||
App.svelte is **NOT** responsible for:
|
||||
- Individual tab content (Overview, Notes, Files) — these are inlined but should be extracted
|
||||
- Note editor internals — `NoteEditorPanel.svelte` + `MarkdownEditor.svelte` handle this
|
||||
- Settings sections — each has its own `Settings*.svelte`
|
||||
- **Files tab** → `FilesTab.svelte` (owns all file browser state, preview, import, rename)
|
||||
- **Notes tab list** → `NotesTab.svelte` (owns note list UI, create form; editor stays in App)
|
||||
- **Overview tab** → `OverviewTab.svelte` (pure display: meta, quick actions, recent items)
|
||||
- **Settings sections** → each has its own `Settings*.svelte`
|
||||
|
||||
## Component Communication
|
||||
|
||||
### Props (parent → child)
|
||||
Data flows down via Svelte `export let prop`
|
||||
|
||||
### Events (child → parent)
|
||||
Children dispatch events via `createEventDispatcher()`
|
||||
|
||||
### Public API (bind:this)
|
||||
Parent gets imperative handle via `bind:this={ref}` and calls:
|
||||
- `ref.publicMethod(args)` — guard with optional chaining: `ref?.method?.(args)`
|
||||
|
||||
## Component Reference
|
||||
|
||||
### OverviewTab (`lib/components/OverviewTab.svelte`)
|
||||
**Props**: `selectedNode`, `notes`, `worklog`, `formatDate`, `nodeKindLabel`
|
||||
**Events**: `createNote`, `addFile`, `createAction`, `switchTab`, `openNote`
|
||||
**State**: None (pure display)
|
||||
|
||||
### NotesTab (`lib/components/notes/NotesTab.svelte`)
|
||||
**Props**: `notes`, `formatDate`
|
||||
**Events**: `submitCreateNote`({title}), `openNote`({note}), `startRename`({noteId, currentTitle}), `deleteNote`({note})
|
||||
**State**: `showCreateNote`, `newNoteTitle`
|
||||
|
||||
### FilesTab (`lib/components/files/FilesTab.svelte`)
|
||||
**Props**: `selectedNode`, `wailsCall`
|
||||
**Events**: `openNote`({id, title}), `refreshParent`({nodeId}), `error`({message})
|
||||
**Public API** (via bind:this):
|
||||
- `resetToNode(nodeId)` — reset to root of given node
|
||||
- `addFile()` — open file picker and import
|
||||
- `loadFolder(folderId)` — load folder contents
|
||||
- `openFileById(fileNodeId)` — find and preview file
|
||||
- `focusItem(nodeId)` — select item by ID
|
||||
- `handleFilesKeydown(e)` — delegate keyboard handling
|
||||
- `resetState()` — full reset (on node change)
|
||||
|
||||
**Internal state** (owned by FilesTab, NOT accessible from App):
|
||||
- `loadingFiles`, `currentFolderId`, `folderStack`, `fileItems`
|
||||
- `previewItem`, `previewContent`, `previewLoading`, `previewError`
|
||||
- `clipboard`, `selectedIds`, `dragIds`
|
||||
- `importing`, `importSummary`, `showImportDialog`, `pendingImportPath`, `pendingImportParent`
|
||||
- `showRename`, `renameId`, `renameValue`, `renameError`
|
||||
- `showConfirm`, `confirmTitle`, `confirmMessage`, `confirmAction`, `cancelAction`
|
||||
|
||||
## State Ownership Rules
|
||||
|
||||
@@ -69,32 +119,20 @@ App.svelte is **NOT** responsible for:
|
||||
- `startupStatus`, `startupChecked`
|
||||
- Navigation state: `navHistory`, `restoringHistory`
|
||||
- Trash browser state: `trashInfo`, `trashCount`, `trashSelectedIds`, `trashFolderId`, `trashFolderStack`
|
||||
- Trash preview: `trashPreviewItem`, `trashPreviewContent`, `trashPreviewLoading`, `trashPreviewError`
|
||||
- Journal state: `journalRows`, `journalSummary`, filters
|
||||
- Worklog modal state: `showWorklogModal`, `wlModal*`
|
||||
- Inbox: `inboxNodes`, `localInboxNodes`, capture state
|
||||
- Links: `links`
|
||||
- Sync: `syncStatus`
|
||||
- `error` (global error banner)
|
||||
- `notes` (loaded by `loadTabData`, passed to NotesTab and OverviewTab as prop)
|
||||
- `worklog` (loaded by `loadTabData`, passed to OverviewTab as prop)
|
||||
|
||||
### App.svelte must NOT directly own (after extraction):
|
||||
- Files tab internal state → `FilesTab.svelte`
|
||||
- Notes tab list UI state → `NotesTab.svelte`
|
||||
- Overview tab content → `OverviewTab.svelte`
|
||||
|
||||
## Component Communication
|
||||
|
||||
### Props (parent → child)
|
||||
Data flows down via Svelte `export let prop`
|
||||
|
||||
### Events (child → parent)
|
||||
Children dispatch events via `createEventDispatcher()`
|
||||
- Suffix `:` in event names means handler in template: `on:openNote={handler}`, NOT call on mount
|
||||
- All events should use `e.detail` to pass data
|
||||
|
||||
### Public API (bind:this)
|
||||
Parent gets imperative handle via `bind:this={ref}` and calls:
|
||||
- `ref.publicMethod(args)` — guard with optional chaining: `ref?.publicMethod?.(args)`
|
||||
- Methods are exposed via `export function` in child component
|
||||
### App.svelte must NOT directly reference:
|
||||
- Files tab internal state (all in FilesTab)
|
||||
- Notes tab create form state (showCreateNote, newNoteTitle — in NotesTab)
|
||||
- Any `fileItems`, `selectedIds`, `currentFolderId`, `folderStack`, etc.
|
||||
|
||||
## Services (Wails API)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user