Commit Graph

1143 Commits

Author SHA1 Message Date
claude code agent 227
41f3944e79 fix(html-embed): execute embeds on public shares; toggle is server-side kill switch
The html-embed feature toggle was enforced CLIENT-side in the NodeView (reads
settings.htmlEmbed from the logged-in workspace), so an anonymous public-share
viewer — who has no workspace context — always saw it as OFF and got a
placeholder instead of the executing embed. That broke the whole point (a
tracker must run for anonymous visitors).

Make it server-authoritative:
- share.service prepareContentForShare (the single path both share-content
  flows use) strips htmlEmbed from served content when the workspace toggle is
  OFF; both callers (updatePublicAttachments host page + lookupTransclusionForShare)
  resolve the toggle once and pass it. Fail-closed: missing workspace -> OFF ->
  stripped.
- NodeView executes whatever it was served in read-only/share mode
  (shouldExecute = !editor.isEditable || htmlEmbedEnabled); the disabled
  placeholder now only shows in the editable editor when OFF.

Net: anonymous share + toggle ON -> server serves the (admin-authored) embed ->
it executes for everyone; toggle OFF -> stripped server-side from every
share-content path (true kill switch); a non-admin embed can never be served
(save-path strip). No XSS regression in the editable editor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 19:49:57 +03:00
claude code agent 227
8fcce6a674 feat(html-embed): per-workspace feature toggle, default OFF
The admin-only raw HTML/JS embed is a deliberate stored-XSS surface, so gate the
whole feature behind a workspace toggle that is OFF by default; it only works
when a workspace admin explicitly enables it.

- settings.htmlEmbed (boolean, default false) + workspace-update field htmlEmbed,
  persisted via WorkspaceRepo.updateSetting with an audit diff. Flipping it is
  admin-only (same Manage Settings CASL as other workspace toggles).
- New gate htmlEmbedAllowed(featureEnabled, role) = featureEnabled && admin/owner.
  All 7 server write paths (create, duplicate, collab onStoreDocument, REST/MCP/AI
  updatePageContent, single + zip import, transclusion unsync) now read the
  workspace's settings.htmlEmbed and strip unless (toggle ON AND admin). OFF
  (default, or a failed/empty workspace lookup) strips htmlEmbed for EVERYONE
  including admins -> existing embeds are cleaned up on next save, none persist.
- Client (defense-in-depth): the /html slash item is hidden unless toggle ON +
  admin; the NodeView executes nothing and shows a 'disabled in this workspace'
  placeholder when OFF; an admin Switch in Workspace Settings -> General with a
  description of the behavior.
- docs/html-embed-admin.md documents the toggle + admin-only + fail-closed
  coedit (a non-admin save strips an admin's embed) + execution semantics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 19:28:39 +03:00
claude code agent 227
caac5c7f36 test(html-embed): exercise the REAL admin-gate write paths + import round-trip
Release-cycle test audit: the strip boundary was tested only via a stand-in
helper re-implemented in the spec, so a deleted/misplaced guard kept CI green
(the missing create() guard was proof). Replace it with tests against real code:
- persistence.extension.onStoreDocument: real ydoc from a rich doc (columns/
  table/mention/htmlEmbed) -> non-admin strip removes only htmlEmbed, every other
  node preserved (data-loss guard); admin keeps; empty fragment no-throw.
- collaboration.handler.updatePageContent: real path, user?.role gate, decoded
  ydoc embed-free for non-admin, kept for admin.
- transclusion unsync: member stripped, admin preserved.
- editor-ext gains a vitest setup (was zero tests) + a markdown round-trip:
  the <!--html-embed:BASE64--> marker -> htmlEmbed node with decoded source, and
  hasHtmlEmbedNode matches it — pinning the marked/turndown shape the import
  strip relies on. tsconfig now excludes specs from the shipped dist.
- Fail-closed identity: source-pinned contracts that the gate keys on
  fileTask.creatorId (zip) / request userId (single) / callerRole (create) /
  authUser.role (duplicate), and missing-user -> strip (services can't load under
  jest's ESM graph; helpers replay the exact predicate).
Adds the verified-safe ^src/ jest moduleNameMapper (identical fail set).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 14:52:29 +03:00
claude code agent 227
e0b3b3d9a5 fix(html-embed): strip htmlEmbed on the plain page-create path too
Release-cycle red-team found the admin-only gate missed PageService.create():
content/textContent/ydoc were derived and persisted without the strip, so any
space member could POST /pages/create with an htmlEmbed node (incl. the
markdown/html <!--html-embed:BASE64--> form) and store executing JS for every
reader. Add the same gate used by duplicatePage: strip htmlEmbed when the
caller is not a workspace admin/owner. Role is plumbed from the controller
(user.role); unknown role => non-admin (strip). All four create paths (create,
duplicate, single import, zip import) plus the update paths are now guarded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 13:09:10 +03:00
claude code agent 227
393bca4dab docs: remove implemented arbitrary-html-embed plan
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 08:54:54 +03:00
claude code agent 227
bd28dbfe2b feat(editor): admin-only raw HTML/CSS/JS embed node
Adds an htmlEmbed block node that renders and executes raw HTML/CSS/JS in the
wiki origin (e.g. an analytics tracker) — the owner-chosen variant C. Because
this is stored-XSS by design, only workspace admins/owners may get such a node
persisted; everyone executes it when reading.

- Node (editor-ext): htmlEmbed atom/isolating block; source stored base64 in
  data-source for lossless HTML<->JSON round-trip. renderHTML emits only the
  encoded marker (never inlines raw markup), so generateHTML/export/search are
  not themselves injection vectors. Registered in BOTH client extensions and
  server tiptapExtensions. Markdown round-trip via an <!--html-embed:b64-->
  comment (turndown) + a marked rule.
- Client NodeView: injects source and re-creates <script> elements so they
  actually run; edit modal; renders in read-only/share too. Slash item is
  admin-gated (adminOnly filtered by the user's workspace role).
- SERVER ENFORCEMENT (the real control — UI gating alone is insufficient):
  stripHtmlEmbedNodes() removes htmlEmbed from any document persisted by a
  non-admin, applied at every write path that introduces content from an
  untrusted author: collab onStoreDocument, REST/MCP/AI updatePageContent,
  single-file import, zip/multi-file import, page duplication, and transclusion
  unsync. Page restore introduces no new content. Public share/readonly viewers
  render fetched (already-stripped) content and do NOT open a collab socket, so
  the only residual is a transient broadcast window to concurrent authenticated
  editors (documented).

Implements docs/arbitrary-html-embed-plan.md (variant C).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 08:54:54 +03:00
claude_code
c8af637654 Merge branch 'develop' of https://gitea.vvzvlad.xyz/vvzvlad/gitmost into develop 2026-06-20 04:19:24 +03:00
claude_code
ddb93525d6 docs: replace CLAUDE.md with AGENTS.md, codify agent workflow (#2)
## What

Renames the agent-guidance file `CLAUDE.md` → `AGENTS.md` (the opencode-standard name) and prepends a process layer on top of the existing technical/architecture content.

## Changes

- **Rename** `CLAUDE.md` → `AGENTS.md` (git detects 64% similarity, history preserved).
- **New top section "Жизненный цикл задачи"** codifies the workflow we just went through:
  1. Sync with `develop`, branch off
  2. Implement (per the system-prompt workflow + subagents)
  3. **Commit ONLY to Gitea and ONLY as `claude_code`** — never as `vvzvlad`, never push to `origin`/`upstream`
  4. Push + PR to `develop`
  5. User merges; agent deletes the task's `docs/backlog/<task>.md`
- **New release-cycle section**: before cutting a version, run the three orchestrator skills (test-orchestrator, review-orchestrator, red-team-orchestrator), fix their findings, then tag per the existing procedure.
- **Credentials cheat-sheet**: agent identity, keychain service name (`gitea-claude-code`), Gitea PR API endpoint, base branch, and do-not-push warnings for `origin`/`upstream`.
- **Fix typo**: repo slug is `gitmost`, not `gtimost` (the remote was redirecting on every push). Local `gitea` remote URL is updated to the canonical form.

## Out of scope

No code changes — docs only.

Reviewed-on: #2
Co-authored-by: claude_code <claude_code@vvzvlad.xyz>
Co-committed-by: claude_code <claude_code@vvzvlad.xyz>
2026-06-20 04:19:03 +03:00
vvzvlad
9fcec4d295 docs: remove backlog doc for broken import formats
Delete the backlog documentation that described the removal of non‑functional DOCX, PDF, and Confluence import features now that the code changes have been merged.
2026-06-20 04:11:19 +03:00
efc36890ec Merge pull request 'refactor(import): remove non-functional DOCX/PDF/Confluence import stubs' (#1) from remove-broken-import-formats-backend into develop
Reviewed-on: #1
2026-06-20 04:07:40 +03:00
claude_code
732aaf54f8 refactor(import): remove non-functional DOCX/PDF/Confluence import stubs
These import paths relied on the private EE module that was deleted from
the repo. In the community build they either threw 'enterprise license'
(DOCX/PDF) or silently no-op'd (Confluence). The frontend buttons were
already removed in 38064064; this cleans up the dead backend stubs.

- import.service.ts: drop processDocx/processPdf methods, their dispatcher
  branches, the pageId computation + insertPage spread, and the now-unused
  moduleRef param/ModuleRef import
- file-import-task.service.ts: drop the Confluence branch and the now-unused
  moduleRef param/ModuleRef import
- import.controller.ts: restrict file extensions to .md/.html and zip
  sources to generic/notion; update the error message accordingly
- file.utils.ts: remove Confluence from the FileImportSource enum
- features.ts: remove the unused CONFLUENCE_IMPORT/DOCX_IMPORT/PDF_IMPORT
  feature keys

The isConfluenceImport logic in import-attachment.service.ts is intentionally
left in place (real shared attachment-parsing code, not a stub); its removal
is a separate, riskier refactor.
2026-06-20 04:05:29 +03:00
vvzvlad
053a9c0d3f docs(public-share): add model & voice input notes to public share plan
docs: add AI agent roles plan documentation
2026-06-19 16:25:21 +03:00
vvzvlad
3d03417c73 fix(import): surface real error cause in /pages/import instead of generic 400
The two catch blocks in importPage() threw an opaque "Error processing file
content" / "Failed to create imported page" BadRequest, hiding the real cause
from the HTTP response. This made a production 400 regression impossible to
diagnose without server log access, and violated the project convention that
errors must never be swallowed.

Extract `${err.name}: ${err.message}` into both the log (full err object kept
for the stack) and the thrown BadRequestException. Inner processMarkdown/
processHTML rethrowing catches and the EE processDocx/processPdf license
catches are left unchanged.

Local reproduction of the happy-dom 14->20 theory failed (full import chain
+ 22 edge cases pass on happy-dom@20.8.9), so the root cause is still pending
the now-visible reason from a recurring 400. Diagnostic script test-import.tsx
added; backlog doc updated with findings.
2026-06-19 16:25:12 +03:00
vvzvlad
d38de4943d docs: update macOS app repo link to gitmost-app
Rename the macOS app repository reference in README.md and README.ru.md
from vvzvlad/docmost-app to vvzvlad/gitmost-app (both the URL and the
link label), reflecting the renamed repo.
2026-06-19 16:24:47 +03:00
vvzvlad
11d789cdea feat(comment): don't auto-open comments panel when adding new comment
- Remove automatic panel opening in handleAddComment
- Remove automatic panel opening in handleAddReadOnlyComment
- Keep panel open on click for existing comments in editor
2026-06-19 16:10:23 +03:00
vvzvlad
b1d48d9d9a fix(client): compact page tree + fix selection highlight alignment
Reduce DocTree row stride from 32px to 26px for a denser sidebar tree,
and fix the selection/hover highlight that looked unbalanced at the
tighter spacing.

Root causes:
- The virtualized <li> had no explicit height, so `.node`'s height:100%
  collapsed to content height; combined with the asymmetric
  `[role="treeitem"] { padding-bottom: 2px }` rule, row content was
  pushed to the top of the highlight pill (icon glued to the top edge).
- NodeMenu / CreateNode action icons used the default Mantine ActionIcon
  size (md = 28px), overflowing the tighter 26px row stride onto
  neighbouring rows.

Changes:
- doc-tree.tsx: rowHeight 32 -> 26; give each row <li> a definite
  height = rowHeight.
- tree.module.css: rowWrapper fills the slot (height:100%); node pill is
  inset and vertically centered (height: calc(100% - 4px)); drop the
  asymmetric [role="treeitem"] padding-bottom.
- space-tree-node-menu.tsx / space-tree-row.tsx: action icons size={20}.
- share.module.css: drop now-dead .treeNode padding-bottom override.

Verified in an isolated browser harness: highlight content is centered
(2.8/2.8px) and nothing overflows the row stride.
2026-06-18 23:08:42 +03:00
vvzvlad
53b7314705 docs(mcp): detail iOS AGPL licensing blocker in plan
Add a dedicated section describing the licensing conflict between the AGPL‑3.0‑licensed web client and App Store DRM/usage rules. Explain why this is a non‑technical blocker, outline possible distribution approaches (server‑loaded client, OTA updates, PWA, sideload), and recommend confirming the chosen path before implementing any iOS wrapper code.
2026-06-18 22:54:23 +03:00
vvzvlad
20867b0689 fix(client): tighten home page list vertical density
Reduce the recent/favorites/created-by-me list tables from
verticalSpacing="sm" (12px Td padding) to a numeric 6px, removing
~12px of extra height per row so the home page lists pack closer
together. The shared RecentChanges table also drives the space home
view, so both stay consistent.
2026-06-18 22:47:05 +03:00
vvzvlad
0457ba817a feat(client): remove Overview item from space sidebar
Drop the Overview home link that sat between the space switcher and
the Pages section in the authenticated space sidebar. Remove the JSX
block and clean up the now-unused imports (UnstyledButton, IconHome,
useLocation, getSpaceUrl) and the local `location` variable.
2026-06-18 22:41:45 +03:00
vvzvlad
7a033b6d6c docs(mcp): document user‑specific auth and full tree toggle
Add markdown files describing the per‑user authentication mechanism and the ability to expand or collapse all nodes in the page tree, improving guidance for developers working with the MCP backlog feature.
2026-06-18 22:38:09 +03:00
vvzvlad
66bb0813a8 fix(client): tighten page tree vertical density
Reduce DocTree default rowHeight from 32px to 26px so sidebar page
tree rows pack closer together. The virtualizer uses rowHeight as the
row stride (estimateSize + translateY), and row content is only ~22px
tall, so the previous 32px left an ~8px gap between nodes. Both the
space tree and the shared (public) tree inherit the default, so both
become denser and stay consistent.
2026-06-18 20:46:23 +03:00
vvzvlad
a0a7d62b59 feat(client): replace space switcher popover with always-visible space grid
Rework the space sidebar:
- remove the "New page" and "Search" menu items (search stays in the app
  header; page creation stays via the "+" button in the Pages section)
- move "Space settings" into a gear icon next to the current space name
- drop the searchable space popover and render all spaces as an
  always-visible grid of fixed-width cards (icon + name), several per row,
  sorted alphabetically, with the active space highlighted
- always inject the active space into the grid so it stays highlightable
  even when the user has more than the 100-space API page limit

The shared SpaceSelect component is left untouched (still used by the
move/copy page modals).
2026-06-18 20:43:07 +03:00
vvzvlad
e7b7f48d35 docs(backlog): add AI chat collapse and comment density docs
Add two new backlog documentation files:
- ai-chat-collapse-on-page-focus.md describing auto‑collapse behavior for the AI chat window.
- comments-panel-density.md outlining UI density improvements for the comments panel.
2026-06-18 20:41:17 +03:00
vvzvlad
850d9ada22 docs(backlog): design server-authoritative realtime tree updates
Add a backlog design note for making page-tree realtime updates
server-authoritative instead of client-relayed.

Problem: page content syncs via Yjs/Hocuspocus (server-authoritative),
but tree create/move/delete is broadcast by the originating browser only,
so non-UI creation paths (AI agent, MCP, REST API, import) and lost-event
races leave other clients' sidebars stale.

The note specifies a WsService.emitTreeEvent broadcaster, WsTreeService
broadcast helpers, a PageWsListener on PAGE_CREATED/SOFT_DELETED/DELETED/
MOVED/RESTORED, event-payload enrichment to avoid the in-transaction
re-fetch race, a dedicated PAGE_MOVED event, removal of the client relay,
plus edge cases, work breakdown, tests, alternatives and open questions.
2026-06-18 20:34:18 +03:00
vvzvlad
1e7a306f96 feat(mcp): add hierarchical tree mode to list_pages
list_pages gains an opt-in `tree` parameter on both surfaces (the
@docmost/mcp server tool and the AI-chat agent tool), which share the
same DocmostClient.listPages. Default behavior (recent-by-updatedAt flat
list) is unchanged.

- client.ts: listPages(spaceId?, limit=50, tree=false); when tree is
  true it requires spaceId (throws a specific error otherwise), walks the
  sidebar tree via the existing bounded/cycle-safe enumerateSpacePages,
  and returns a nested tree; limit is ignored in tree mode.
- lib/tree.ts: new pure buildPageTree() — lean nodes { id, slugId, title,
  children? }, children sorted by position (code-unit order), orphans
  promoted to roots, cycle-safe.
- index.ts + ai-chat-tools.service.ts: expose `tree` in the tool schemas
  and descriptions; docmost-client.loader.ts: mirror the new signature.
- tests: add packages/mcp/test/unit/tree.test.mjs (nesting, ordering,
  lean shape, orphan promotion, cycle/self-reference safety).
- rebuild @docmost/mcp (build/ is tracked and loaded at runtime).
2026-06-18 20:30:00 +03:00
vvzvlad
8178d21c00 fix(client): glue version string to logo on the app header baseline
Wrap the logo link and the APP_VERSION text into a single bottom-aligned
Group so they read as one lockup ("gitmost v0.9..."). Move the version
styling into a new .brandVersion CSS class: shrink it from 12px to 10px,
keep the dimmed color and selectability, and lift it via margin-bottom so
its text baseline sits on the wordmark baseline of the 30px desktop logo
(derived from the logo SVG geometry). Drop the redundant lh prop.
2026-06-18 20:13:46 +03:00
vvzvlad
5d8860e47b docs(backlog): clean up backlog documentation
Remove outdated process sections from several backlog markdown files and add new backlog items for AI chat step limits, endpoint status config, and API key field UI improvements.
2026-06-18 20:02:01 +03:00
vvzvlad
9b8ac430b2 fix(ai-chat): preserve scroll position during agent message streaming
The transcript force-scrolled to the bottom on every streamed delta because
the auto-scroll effect ran unconditionally whenever the messages array identity
changed. Scrolling up to read earlier messages was impossible — each token
yanked the view back down.

Implement a "stick to bottom" pattern in MessageList:
- track whether the viewport is pinned to the bottom via a scroll listener
  (pinnedToBottomRef, BOTTOM_THRESHOLD = 40px);
- only auto-scroll while pinned; a freshly sent user message always re-pins;
- attach the scroll listener via a [hasScrollArea] dependency so a brand-new
  empty chat (whose ScrollArea mounts only after the first message) wires it up;
- guard the effect's own scrollTop write (programmaticScrollRef) so it is not
  misread as a user scroll.
2026-06-18 19:54:40 +03:00
vvzvlad
f96df1c540 feat(ai-chat): show current context size instead of total tokens spent
The floating AI-chat header badge summed metadata.usage (AI SDK
totalUsage, all steps) across every assistant row, showing the
cumulative tokens SPENT — which grows each turn as history is re-sent.
Replace it with the conversation's CURRENT context size.

- server: persist metadata.contextTokens in streamText onFinish from the
  final-step `usage` (inputTokens + outputTokens ≈ current context
  window occupancy); keep usage: totalUsage for back-compat/fallback
- client: derive the badge from the most recent assistant row's
  contextTokens (fallback to that row's usage total for older chats)
  instead of summing all rows
- types: add metadata.contextTokens to IAiChatMessageRow
- i18n: rename badge label "Tokens used in this chat" -> "Current
  context size" (en-US)

No DB migration needed (metadata is a JSON column).
2026-06-18 19:54:34 +03:00
vvzvlad
411671bad2 docs(backlog): STT extra providers + async transcription roadmap
Add docs/backlog/stt-providers-and-async.md: how to add new synchronous STT
request formats (Deepgram, native Gemini, ElevenLabs) via the explicit
sttApiStyle axis, which providers are inherently async and don't fit the
current sync model, and a target job-based async architecture (BullMQ job
table, sync+async unification, polling -> push -> live streaming) with the
migration path and security/cleanup considerations.

Add docs/streaming-dictation-plan.md — a design document for true
"text appears as you speak" dictation via the OpenAI Realtime API.

- Maps the current batch dictation flow (client MediaRecorder -> single
  blob -> POST /ai-chat/transcribe) and why streaming is impossible there.
- Documents the Realtime API contract (transcription session, ephemeral
  token, pcm16 audio, input_audio_buffer.append, input_audio_transcription
  delta/completed events, server_vad).
- Recommends a server-side WS proxy transport (key stays server-side,
  SSRF-guarded, provider-agnostic via sttBaseUrl) over direct browser
  WebRTC, and a ProseMirror decoration for interim text with final-only
  commit to avoid polluting Yjs collab/history.
- Covers config additions, AudioWorklet PCM16 capture, security per repo
  conventions, edge cases, phased rollout, risks, and impacted files.
2026-06-18 19:44:16 +03:00
vvzvlad
01a5a4b5d2 refactor(ai): explicit STT request format instead of OpenRouter host-sniffing
Replace the implicit `hostname endsWith openrouter.ai` detection with an
explicit, admin-chosen provider field `sttApiStyle` ('multipart' = OpenAI-
compatible multipart /audio/transcriptions; 'json' = OpenRouter-style JSON +
base64 input_audio). The transcription path now branches on the stored field,
not on the URL — nothing hidden from the admin.

- ai.types: add SttApiStyle + STT_API_STYLES; field on AiProviderSettings and
  MaskedAiSettings (resolved via ResolvedAiConfig).
- update-ai-settings.dto: validate sttApiStyle with @IsIn(STT_API_STYLES).
- ai-settings.service: plumb sttApiStyle through resolve()/getMasked() and the
  non-secret update whitelist; workspace.repo: add it to the ALLOWED array so it
  persists.
- ai.service: drop isOpenRouter(); transcribe() branches on cfg.sttApiStyle;
  rename helper to transcribeJsonBase64 with provider-neutral error text and a
  BadRequestException (400) when the base URL is missing for the JSON style.
- client: SttApiStyle type on IAiSettings/IAiSettingsUpdate; "Request format"
  Select on the Voice/STT settings card; i18n.
2026-06-18 19:40:05 +03:00
vvzvlad
77249d59c6 feat(ai): OpenRouter STT support + real error surfacing + STT endpoint test
- ai.service: route *.openrouter.ai STT to its JSON+base64
  /audio/transcriptions API; keep the OpenAI multipart path (AI SDK) for
  OpenAI/self-hosted whisper. Unify transcription behind transcribe().
- /transcribe controller: surface the real provider/transport reason
  (describeProviderError) instead of an opaque 500; preserve HttpException.
- testConnection: add an 'stt' capability (silent-WAV probe) + DTO; client
  gets a Test endpoint button and status dot on the Voice/STT card.
- useDictation: log full errors to the console and show the real reason
  (mic start + transcription paths); handle NotReadable/Abort and missing
  mediaDevices.
- docs(CLAUDE.md): require full error logging + specific user-facing messages.
2026-06-18 19:26:35 +03:00
vvzvlad
ef90655657 docs: add error handling guidelines to docs
Add a documentation bullet that enforces comprehensive error logging and user‑facing messages, ensuring caught errors are fully logged and presented with specific, human‑readable explanations rather than generic messages.
2026-06-18 19:10:03 +03:00
vvzvlad
16dc7ea5cf docs(readme): add voice dictation to README and delete its plan
The README files now list Voice dictation as a completed feature () instead of an upcoming one (🔭). Consequently, the detailed `voice-dictation-plan.md` documentation has been removed. This reflects that the feature is ready and no longer merely a plan.
2026-06-18 19:00:01 +03:00
vvzvlad
beb95cbbbb docs: add migration ordering guidance
Add a section describing how Kysely runs migrations in alphabetical (timestamp) order and the need to verify migration timestamps when merging branches. This helps prevent migration ordering errors and boot failures.
2026-06-18 18:56:39 +03:00
vvzvlad
5af40e0ee5 refactor(db): replace STT credentials migration
Remove the outdated 20260618T130000 migration file and add the updated
20260618T160000 version to correct the timestamp and ensure proper ordering.
2026-06-18 18:54:24 +03:00
vvzvlad
9dd5bf680d docs(readme): update roadmap with new feature plans
Update the README files to list newly planned features on the roadmap, including page templates, a public‑share AI assistant, and academic‑style footnotes. This improves documentation of upcoming functionality.
2026-06-18 18:48:31 +03:00
vvzvlad
9766c0fee6 docs: add public share assistant plan
Add a detailed design and implementation plan for an AI assistant that
operates on publicly shared document trees. The document outlines the
feature scope, architecture, security considerations, and remaining work,
providing context for future development.
2026-06-18 18:47:56 +03:00
vvzvlad
6bbddc636f docs: add footnotes feature plan and list it on the roadmap
Add docs/footnotes-plan.md describing the footnotes feature design:
reference + definitions model (Markdown/pandoc-style) chosen over the
self-contained inline-atom approach for Yjs/Hocuspocus collaboration
safety. Covers the three nodes (footnoteReference / footnotesList /
footnoteDefinition), decoration-based numbering, hover-popover reading,
HTML/Markdown round-trip, MCP schema mirror + commentsToFootnotes
migration, edge cases, affected files, phased plan and test strategy.
2026-06-18 18:46:33 +03:00
vvzvlad
a473df7f32 docs: add design plan for live page-template embeds
Add docs/page-templates-plan.md describing a whole-page live
transclusion feature: pages flagged is_template, a new pageEmbed
node referencing a source page, a whole-page lookup endpoint reusing
the existing transclusion access-control and share paths, reference
sync, duplicate remap, and cycle/deletion/access/export edge cases.
Decision: separate pageEmbed node over extending transclusionReference.
2026-06-18 18:45:57 +03:00
vvzvlad
874bdd021c feat(ai): server-side voice dictation (STT) with mic in chat and editor
Add push-to-talk voice dictation that transcribes recorded audio on the
server via the workspace's OpenAI-compatible AI provider (Whisper /
gpt-4o-transcribe / self-hosted whisper), then inserts the text.

Backend:
- New `stt_api_key_enc` column + migration; STT creds parity with chat/
  embeddings (sttModel/sttBaseUrl/sttApiKey, write-only key, fallbacks to
  chat baseUrl/key). Both provider whitelists updated (service + repo).
- AiService.getTranscriptionModel + AiTranscriptionService.
- Gated POST /ai-chat/transcribe (dictation flag → 403, JWT + workspace
  scope + throttle, 25MB cap, MIME whitelist, never logs audio/key).
- New `settings.ai.dictation` workspace flag (DTO + service + audit).

Frontend:
- Wire up the Voice/STT settings card (model/base URL/key) and the
  Voice-dictation toggle.
- New `features/dictation`: useDictation (MediaRecorder state machine),
  MicButton, transcribe service; integrated into the chat composer and a
  new editor-toolbar dictation group, both gated by ai.dictation.
2026-06-18 18:45:33 +03:00
vvzvlad
49eba22201 docs(claude): document the release procedure
Add a "Cutting a release" subsection to CI / release: version selection
(SemVer), synchronized package.json bump (root + client + server; mcp is
independent), CHANGELOG update, lightweight v-tag, and push that triggers
release.yml. Document moving a misplaced tag via git tag -f + force-push,
and note the git tag (not package.json) is the source of truth for the
displayed version.
2026-06-18 18:09:15 +03:00
vvzvlad
c6b878c514 0.91.0
Bump root, client and server package versions 0.90.1 -> 0.91.0 to match the
v0.91.0 release tag. packages/mcp keeps its independent 1.0.0 version.
2026-06-18 18:07:54 +03:00
vvzvlad
0ceea15e87 feat(ai-chat): wider default window, larger max size, smaller font
Make the floating AI chat window open at a larger default size and
allow stretching it further, plus shrink the fonts.

- ai-chat-window.tsx: DEFAULT_WIDTH 362->540, DEFAULT_HEIGHT 602->680;
  clamp the default width to the viewport in computeInitialGeom()
  (symmetric with the existing height clamp) to avoid overflow on
  narrow screens.
- ai-chat-window.module.css: raise resize caps (max-width 560->900px,
  max-height 880->1100px); base font-size 12->11px.
- ai-chat.module.css: chat content font .messages sm->xs.
2026-06-18 18:06:48 +03:00
vvzvlad
9039a50da9 docs: add CLAUDE.md for AI agents
Add a root CLAUDE.md describing the Gitmost project for Claude Code and
other AI-agent tooling. Covers: the AGPL-only/no-EE fork philosophy and the
"internal identifiers stay docmost" naming gotcha; the pnpm+Nx monorepo
layout; build/lint/test (incl. single-test) and migration commands; and the
big-picture architecture — the two server processes (API on PORT, collab on
COLLAB_PORT via Hocuspocus/Yjs), NestJS module layout, Kysely/Postgres+
pgvector/Redis persistence, the shared editor-ext (client + server; the MCP
package vendors its own schema mirror), and the two AI subsystems (embedded
/mcp server and the CASL-scoped AI chat agent with RAG).
2026-06-18 18:05:48 +03:00
vvzvlad
84f99e3ec1 chore(gitignore): add .claude/worktrees/ to ignore list 2026-06-18 17:53:40 +03:00
vvzvlad
06648d91bb feat(ai-chat): copy agent chat as Markdown to clipboard
Add a header button to the AI agent chat window that copies the active
conversation to the clipboard as Markdown, including the request
internals already persisted client-side — tool calls with their
input/output, per-message token usage, and finish/error info. No new
network call and no server/DB change: it serializes the already-loaded
persisted message rows.

- New util chat-markdown.ts (renamed from export-chat.ts): pure
  buildChatMarkdown() serializer reusing the tool-parts helpers so tool
  labels match the on-screen labels; fence() escapes embedded code
  fences.
- ai-chat-window.tsx: Copy button (shown only for a saved chat with
  loaded rows) using the project useClipboard hook; toggles a check
  icon on success and shows the standard "Copied" notification. Drag is
  unaffected (startDrag ignores button clicks).
- en-US: add "Copy chat" key, drop the obsolete "Export chat".
2026-06-18 17:53:18 +03:00
vvzvlad
8f5be58f9b feat(ai-chat): move AI chat icon into the global top bar
Move the AI-chat toggle icon (IconSparkles) from the page header menu
into the global top bar, placed next to the notifications icon. The
"AI chat enabled" gate (workspace.settings.ai.chat) is preserved, and
the icon style is aligned with the neighbouring notifications icon
(subtle, size sm). As a result the entry point is now available on all
routes instead of only on page routes.

- app-header.tsx: render the gated AI-chat ActionIcon before
  NotificationPopover; wire it to aiChatWindowOpenAtom.
- page-header-menu.tsx: remove the old AI icon block and its now-unused
  imports/locals.
2026-06-18 17:50:42 +03:00
vvzvlad
3d9c9daf98 feat(ai-chat): focus composer on chat creation
Add autoFocus to the chat composer Textarea so a freshly created chat
(window open, "New chat", chat switch — all remount ChatThread via key)
lands with the cursor ready in the input field, letting the user type
immediately without clicking into it.
2026-06-18 05:51:24 +03:00
vvzvlad
cee9f6fb6e docs(backlog): add AI chat backlog documentation 2026-06-18 05:50:10 +03:00