mirivlad
700e4dae5b
fix: global search case-insensitive + keyboard layout swap
...
Unified search normalization across InternalLinkPicker and GlobalSearch:
1. GlobalSearch.svelte: multi-variant search (same as InternalLinkPicker)
- expandKeyboardVariants() for RU/EN layout swap
- Parallel Search queries with dedup by type+nodeId+targetId+title
- 180ms debounce preserved
2. Backend: fix LOWER() in SQL for links/actions
- Replace LOWER(column) LIKE with lowercased columns (title_lower, url_lower, etc.)
- Migration 020: add lowercased columns + indexes for links and actions
- BackfillLinksLower() + BackfillActionsLower() in storage.go
- Update INSERT in bindings_links.go and action.go to populate lowercased columns
3. FTS5 search: Unicode case-insensitive
- Index lowercased title/content/tags in search_index
- sanitizeFTS() now lowercases query before MATCH
- RebuildFTS() called after migrations
4. Case-insensitive search for nodes (already done in previous commit, verified):
- title_lower column with Go strings.ToLower
- Search() queries title_lower with lowercased query
All test suites PASS, full build OK.
2026-06-15 10:52:34 +08:00
mirivlad
88eb99e9af
fix: verstak:// links in preview, case-insensitive search, keyboard layout swap
...
1. Fix verstak:// links rendered as blocked/strikethrough in markdown preview:
- Changed href from 'javascript:void(0)' to hash-based '#verstak-type-id'
- DOMPurify no longer strips the link; click handler uses data-verstak-href
- CSS already handles .md-link--internal with cyan color, no strikethrough
2. Add markdown label escaping for internal link picker:
- New escapeMarkdownLabel() in markdown.ts escapes [ ] ( )
- Applied in InternalLinkPicker.selectResult() before inserting markdown
3. Fix case-insensitive search for RU/EN:
- Add title_lower column (migration 019) populated by Go strings.ToLower
- BackfillTitleLower() runs after migrations to populate existing rows
- Search() now queries title_lower with Go-level lowercase (Unicode-aware)
- insertNode() and UpdateTitle() populate title_lower automatically
- New migration 019 + BackfillTitleLower in storage.go
- Tests: TestSearchCaseInsensitive, TestSearchFindsCreatedNode
4. Add keyboard layout swap search support:
- New keyboardLayout.ts utility with RU↔EN QWERTY mapping
- expandKeyboardVariants() generates original + swapped + lowercased variants
- InternalLinkPicker.search() queries all variants in parallel, deduplicates by ID
- Examples: dthcnfr → верстак, руддщ → hello
Files changed:
- markdown.ts: hash href + escapeMarkdownLabel export
- InternalLinkPicker.svelte: label escaping + layout swap search
- keyboardLayout.ts: new RU/EN layout swap utility
- repository.go: title_lower in Search/insertNode/UpdateTitle
- storage.go: migration019 + BackfillTitleLower
- migrations_019.sql.go: new migration
- search_test.go, repository_test.go: new tests
2026-06-15 10:39:44 +08:00
mirivlad
7521eea109
feat: full Internal Link Picker with search and type filter
...
Replace broken ObjectPickerModal and manual modal with proper
InternalLinkPicker component:
- Search field with debounced SearchNodes API calls
- Type tabs: Дело, Заметка, Файл, Секрет (disabled)
- Results list showing title + path, keyboard navigation
- Inserts [Title](verstak://type/id) at cursor position
- No layout breakage — picker is a normal modal via position:fixed
- Escape/Cancel close picker cleanly
- bind:this on NoteEditorPanel → MarkdownEditor.insertText()
Also:
- MarkdownEditor: added public insertText() method + bind:this
- NoteEditorPanel: added bind:this on MarkdownEditor + public insertText()
- Removed manual modal, insertInternalLinkMarkdown(), document.querySelector
2026-06-15 09:57:11 +08:00
mirivlad
0fdf77ce03
fix: stabilize markdown notes — internal link modal, rename UI, trash integration
...
- Replace broken ObjectPickerModal with simple inline modal (Label+URL fields)
- Insert internal link at cursor position in textarea
- Add rename button in note editor header and note cards
- Add delete button on note cards with confirm dialog
- Integrate DeleteNote with shared trash (.verstak/trash/) via files.TrashFile()
- Remove hidden .verstak/trash/notes/ folder — notes use unified trash now
- Fix purgeTrashNode to clean file-record-based trash entries (notes/files)
- Add activity + sync ops to DeleteNote binding
- Add files.TrashFile() public method
- Update i18n keys for note.rename, note.deleteConfirm, internal link modal
- AssertContained: symlink-aware path containment check
- Update tests: shared trash, file record missing flag, collision on rename
- All go test ./... pass, frontend build passes, GUI binary built
2026-06-15 09:19:26 +08:00
mirivlad
21130c6f1e
debug: log typeof events and JSON.stringify in both parent and iframe
2026-06-08 13:57:45 +08:00
mirivlad
82c2588449
debug: replace render() with minimal test to verify innerHTML works
2026-06-08 13:46:11 +08:00
mirivlad
c03e2e2961
debug: add WriteDebugLog after postToIframe to confirm delivery
2026-06-08 12:31:45 +08:00
mirivlad
35e23d75fa
debug: add step-by-step logging in loadCalendarData to trace where error occurs
2026-06-08 12:23:51 +08:00
mirivlad
5069472e19
debug: add logging to get_categories + better error details in CalendarPluginPage
2026-06-08 12:01:29 +08:00
mirivlad
f769daa617
fix(plugins): JSON-serialize CallFunctionJSON return values + backward compat Lua args
...
Root cause: CallFunctionJSON used .String() on Lua return values, which
for tables produces 'table: 0x...' — not valid JSON. Frontend does
JSON.parse() on the result and silently caught the parse error.
Fix:
- runtime.go: convert Lua return value to JSON via luaValueToGo +
json.Marshal so tables become proper JSON arrays/objects
- main.lua: add backward compat in get_events() and update_event()
to accept both positional args (start, end) and table params
- CalendarPluginPage.svelte: show errors in UI instead of silent catch;
restructure template to always show iframe + error overlay
2026-06-08 11:31:18 +08:00
mirivlad
fddbd3a98a
fix calendar: table params for Lua functions + remove duplicate header
...
- CalendarPluginPage.svelte: removed <h2>pluginName — pageLabel</h2> (AppHeader already shows title)
- main.lua: all API functions now accept a single table parameter:
- get_events(params) — reads params.start_date or params.start
- get_event(params) — reads params.id
- update_event(params) — reads params.id + mutable fields
- delete_event(params) — reads params.id
- get_expanded_events(params), get_calendar_events(params) — same
- get_events_day(params) — reads params.date
- create_event(opts) — already worked, no change
- Backward compatible: get_events accepts both start/start_date and end/end_date keys
2026-06-08 11:18:28 +08:00
mirivlad
b1d1defebe
release infra: build scripts, Firefox signing, plugin fixes
...
- .gitignore: release/, .env, *.xpi, node_modules/
- .env.example: template for AMO credentials
- extension-firefox/package.json: web-ext scripts (lint, sign)
- extension-firefox/manifest.json: gecko.id + update_url + data_collection_permissions
- scripts/build.sh: renamed binaries (verstak, verstak-server), release target
- scripts/sign-firefox-xpi.sh: AMO signing with --self-hosted
- scripts/release-firefox-xpi.sh: signed XPI + updates.json generation
- scripts/release.sh: full release pipeline (DEB/RPM/checksums/git tag)
- VERSION: 0.1.0
- README.md: Firefox extension & release sections
Plugin fixes:
- internal/core/plugins/manager.go: auto-create .verstak/plugins/ dir
- frontend/src/lib/SettingsSidebar.svelte: remove 'plugins' from disabled + active left border
- frontend/src/lib/SettingsPlugins.svelte: force ListPlugins refresh on toggle
- frontend/src/lib/SettingsWindow.svelte: onPluginToggle callback
- frontend/src/App.svelte: refreshSystemViews on plugin toggle
- frontend/src/lib/CalendarPluginPage.svelte: visible error icon
2026-06-08 11:07:29 +08:00
mirivlad
45cfe1b0a6
fix: финальный cleanup Lua plugin lifecycle
...
1. ActivatePlugin → error return:
- Возвращает ошибки при создании VM, загрузке main.lua, scheduler setup
- on_init failure = non-fatal (logged, activation continues)
- SetPluginEnabled сохраняет EnabledPlugins в config ТОЛЬКО после успешной активации
- При ошибке активации — rollback (deactivate + не сохраняем в config)
2. CallPluginFunction fully thread-safe:
- Новый метод LuaVM.CallFunctionJSON(segments, paramsJSON)
- JSON→Lua conversion происходит под vm.mu (внутри lock)
- Убраны parseParamsToLua/goToLua из bindings_plugins.go
- goToLua перенесён в runtime.go (под lock)
3. PluginPage → CalendarPluginPage:
- Компонент явно календарный (get-events/create-event/update-event/delete-event)
- Переименован для ясности
- Console log префиксы обновлены
4. Тесты:
- TestSetPluginEnabled_ActivateFails_NoConfigSave: проверяет что при ошибке
активации плагин НЕ сохраняется в EnabledPlugins
- TestActivatePlugin_ErrorReturn: проверяет все режимы ошибок
- TestCallFunctionJSON_ThreadSafe: JSON object/array/empty params
- TestDeactivatePlugin_Idempotent: двойная деактивация = no-op
- TestInitRuntimes_SkipsDisabled: только Enabled плагины активируются
2026-06-07 22:58:26 +08:00
mirivlad
d83c8c80e1
fix: второй стабилизационный проход Lua plugin lifecycle
...
1. Enabled/Active state separation:
- Enable() sets Enabled=true (persisted in config), does NOT create runtime
- ActivatePlugin() checks Enabled && !Active, creates VM + scheduler
- DeactivatePlugin() stops runtime, keeps Enabled=true
- InitRuntimes() iterates Enabled plugins, sets Active=true after creation
- SyncConfig() restores Enabled from config, does NOT touch Active
2. ActivatePlugin: добавлен vm.SetServices(m.Services)
3. Discover: атомарная замена списка (newPlugins slice), нет дублирования
4. CallPluginFunction: thread-safe через LuaVM.CallFunction (vm.mu + callWithTimeout)
5. Uninstall активного плагина: полная деактивация (StopScheduler → on_shutdown → CloseVM → Active=false)
6. GetPluginPanelHTML: валидация panel path (no absolute, no .., must be .html, must be within plugin dir)
7. PluginPage: убран hardcoded 'calendar-plugin', используется funcPrefix из pluginName
Тесты:
- security_test.go: +8 тестов (FullLifecycle, ActivatePlugin_Services, Discover_Idempotent,
ReloadPlugins_NoDuplicates, CallPluginFunction_Timeout, Uninstall_ActivePlugin,
GetPluginPanelHTML_PathTraversal, FullLifecycle_EndToEnd)
- manager_test.go: обновлены тесты под новую семантику Enabled/Active
2026-06-07 20:49:43 +08:00
mirivlad
4df83cd361
security: стабилизационный аудит Lua plugin system
...
Исправления:
- Install: идемпотентность (no duplicates in InstalledPlugins)
- ReloadPlugins: StopSchedulers + CallShutdownHooks перед CloseRuntimes
- StopSchedulers: обнуление scheduler=nil после остановки
- Scheduler.Stop: обнуление tasks после wg.Wait
- Lua sandbox: блокировка package.loadlib/seeall/preload/loaders/loaded/path/cpath/config/searchpath
- Lua sandbox: блокировка load (глобальная функция)
- CallPluginFunction: валидация funcName (regex [a-zA-Z_][a-zA-Z0-9_]*, max 3 segments)
- CallPluginFunction: убрана строковая сборка Lua-кодa, вызов через PCall напрямую
- PluginPage.svelte: проверка e.source === iframeEl.contentWindow
- PluginPage.svelte: type checking для msg.source, msg.action
Тесты:
- security_test.go: 18 новых тестов (sandbox, lifecycle, validation)
- Все существующие тесты проходят
Документация:
- docs/plugins-security.md: модель безопасности, sandbox, протокол, lifecycle
2026-06-07 19:19:44 +08:00
mirivlad
c443ca23c5
fix: PluginPage.svelte — замена CallPluginAction на CallPluginFunction с dotted path
...
PluginPage.svelte использовал несуществующий Wails binding CallPluginAction.
Заменён на CallPluginFunction с правильным dotted path (calendar.get_events и т.д.),
что соответствует сигнатуре bindings_plugins.go.
Frontend пересобран, go build + go test ./... — всё зелёное.
2026-06-07 16:56:28 +08:00
mirivlad
c5505ee43c
feat: Firefox-расширение Verstak Bridge
...
- extension-firefox/manifest.json — Manifest V3 для Firefox
(browser_specific_settings.gecko, background.scripts)
- extension-firefox/background.js — browser.* API с chrome.* полифиллом
- Стабильный device_id через crypto.getRandomValues (6 байт hex)
- Фильтрация about:, moz-extension:, resource: и пр. внутренних URL
- device_id с префиксом 'firefox-' для различения в activity
- extension-firefox/popup/ — общие popup HTML/CSS/JS (копия Chrome)
2026-06-06 19:08:31 +08:00
mirivlad
f88376264d
fix: reorder journal worklog sections
2026-06-06 02:53:36 +08:00
mirivlad
40c0953904
fix: skip deleted entries in navigation history
2026-06-06 02:42:20 +08:00
mirivlad
0cd8a79049
feat: restore global search in app header
2026-06-06 02:39:29 +08:00
mirivlad
cf770262e5
fix: reset capture drag state reliably
2026-06-06 02:30:54 +08:00
mirivlad
a37afd3b67
fix: trash integrity for TypeFile nodes — file record soft-delete, correct preview/restore
2026-06-05 17:31:18 +08:00
mirivlad
64e6c6f735
fix: trash file preview, visual CSS, virtual folder model
...
- Added resolveTrashPath() backend function: walks ancestor chain to find
files inside deleted/moved folders (flat trash directory)
- Added TrashFsPath to TrashNodeDTO, computed during ListTrash via
parent-to-child propagation of physical trash path
- Fixed visual CSS for trash rows: button reset (no white bg, transparent,
inherit font/color), hover styles match app dark theme
- Root view filters out descendant nodes (only shows top-level items)
2026-06-05 17:05:35 +08:00
mirivlad
5257789a4d
fix: trash duplicate path, journal tabs, today undefined, mouse back, inbox search
...
- Trash: removed duplicate fsPath display, folders/files open by clicking icon/name
- Trash: removed separate 'Open' button, only restore/delete right-aligned
- Trash: added ReadTrashFileContent binding + preview for files
- Trash: breadcrumb path under title, compact date display
- Journal: split into 'Предложения' + 'Журнал работы' tabs
- Journal: suggestions tab opens by default
- Today: fixed undefined in feed (null-guard on eventType/title)
- Today: improved event type label readability (blue badge style)
- Today: folder_deleted clicks navigate to trash
- Mouse Back: added mouseup fallback for Wails compat, handle button 3/4
- SearchNodes: case-insensitive with LOWER()
- Inbox assign: added search hint placeholder
2026-06-05 16:49:00 +08:00
mirivlad
c512ada386
cleanup: remove stale frontend-dist assets, fix warnings
2026-06-05 16:21:21 +08:00
mirivlad
2ed2ecf77a
Today screen: tabs (feed, suggestions, in-progress, captured) + inbox sort/group
...
- Новый экран 'Сегодня' разбит на 4 вкладки: Лента, Предложения,
В работе, Захвачено
- Лента отображает события за сегодня с кликабельными сущностями
- Предложения вынесены в отдельную вкладку (только предложения)
- В работе: изменённые файлы/заметки/действия за сегодня с сортировками
- Захвачено: захваченные элементы за сегодня с сортировками
- Неразобранное: сортировка по дате/имени/типу с направлением
- Неразобранное: переключатель 'Группировать по месту захвата'
- TodayScreen.svelte: новый компонент с 4 вкладками
- Новые i18n ключи для вкладок и сортировок
- Backend: ListTodayInProgress, ListTodayCaptures bindings
- Все переходы из вкладок ведут в соответствующее место программы
2026-06-05 16:17:22 +08:00
mirivlad
c8aaf36533
fix: stabilize trash navigation and action icons
2026-06-05 14:41:40 +08:00
mirivlad
1fa009b1e2
feat: complete trash restore and batch actions
2026-06-05 12:43:30 +08:00
mirivlad
10b287de7b
feat: aggregate journals across node subtrees
2026-06-05 12:37:25 +08:00
mirivlad
23f517dee3
feat: simplify inbox actions and group task tabs
2026-06-05 12:32:36 +08:00
mirivlad
6d15639b41
fix: normalize bare URLs in capture flow
2026-06-05 12:29:19 +08:00
mirivlad
56ef211418
chore: move app icons into frontend assets
2026-06-05 12:25:47 +08:00
mirivlad
4755d3199d
chore: update embedded gui assets
2026-06-05 07:51:00 +08:00
mirivlad
6eaa4cda49
feat: assign and delete inbox artifacts
2026-06-05 02:15:27 +08:00
mirivlad
a96a316883
feat: capture files and images in inbox
2026-06-05 02:06:21 +08:00
mirivlad
326f6f283d
feat: capture clipboard links in inbox gui
2026-06-05 01:55:38 +08:00
mirivlad
d6ef3a973a
feat: model inbox capture artifacts
2026-06-05 01:40:08 +08:00
mirivlad
2e86229350
fix: restrict inbox to captured artifacts
2026-06-05 01:35:27 +08:00
mirivlad
cc83cd3476
feat: expose trash in gui
2026-06-05 01:05:57 +08:00
mirivlad
035f877280
feat: add interactive inbox view
2026-06-05 00:59:57 +08:00
mirivlad
02d68ca3f4
feat: edit suggestions before accepting worklog
2026-06-05 00:53:13 +08:00
mirivlad
eb6a861310
feat: edit and delete worklog entries
2026-06-05 00:48:12 +08:00
mirivlad
cb6c06fdc5
fix: style workspace settings actions
2026-06-04 19:34:03 +08:00
mirivlad
cc157a2d36
fix: stabilize settings icons and back label
2026-06-04 07:47:40 +08:00
mirivlad
c40d8c9dd3
fix: show sync warnings in settings
2026-06-04 03:53:17 +08:00
mirivlad
7e709e140d
build: refresh embedded gui assets
2026-06-04 03:49:12 +08:00
mirivlad
a69dc845e6
fix: vault init on startup; add nil guards to all bindings; fix SA_ONSTACK signal crash; deduplicate settings button; add i18n for vault error
2026-06-04 00:37:14 +08:00
mirivlad
f92394e3d7
feat: settings window polish, sync widget fix, dark form controls
...
- Fix: settings overlay uses on:click|self so sidebar clicks don't close it
- Fix: openSettings(section) supports opening at specific tab
- Fix: 'Настроить' opens Settings → Синхронизация instead of Общие
- Style: dark theme select with custom arrow, global :global() CSS
- Style: settings cards, section descriptions, button/layout polish
- Style: settings gear buttons (icon-button pattern, 32px, soft hover)
- Style: settings sidebar with disabled stubs, consistent icons
- i18n: add generalDesc, workspaceDesc, appearance, localization keys
2026-06-03 23:09:40 +08:00
mirivlad
e30a75c5a0
fix: openActivityTarget navigates to parent folder and previews files
...
- resolveActivityTarget uses 'targetId'/'targetPath' for all types
(note, file, folder) instead of type-specific property names
- openActivityTarget for files: resolves the file node via
GetNodeDetail, navigates to its parent folder in the Files tab,
then auto-previews the file if it's a previewable type
- For root-level files (no parent_id): loads root items
- Removed spurious OpenFolder(targetPath) call that silently failed
because OpenFolder expects a node ID, not a filesystem path
2026-06-03 17:32:18 +08:00
mirivlad
4ec03c849f
fix: openActivityTarget now loads fileItems before showing Files tab
...
selectNode() resets fileItems=[] and activeTab='overview'. Setting
activeTab='files' programmatically does not trigger the tab click
handler that calls loadFolder(), so the file tree stays empty.
Fix: explicitly call await loadFolder(selectedNode.id) in the files
branch of openActivityTarget.
Also: unified resolveActivityTarget return shape to always use
targetId/targetPath regardless of targetType.
2026-06-03 17:23:42 +08:00