Commit Graph

174 Commits

Author SHA1 Message Date
claude code agent 227
20a1780977 test(ai-roles): cover role-resolution, CASL gate, model override; hide disabled badge
Release-cycle test audit found the role feature's security-critical paths
untested. Adds real unit tests (against the actual functions):
- resolveRoleForRequest invariants: role comes from chat.roleId not body.roleId
  (no per-turn swap), lookup scoped to workspace.id, disabled/soft-deleted role
  -> null, new-chat uses body.roleId, stale chatId falls back.
- CASL admin gate: non-admin create/update/delete -> Forbidden and service not
  called; admin delegates with workspace.id; list() is member-reachable.
- roleModelOverride: unknown driver dropped (never reaches getChatModel's
  throwing default), valid override passes through, blanks ignored.
- getChatModel override success path (cross-driver fetch + decrypt; chatModel-
  only reuse), and service update/remove cross-workspace 'not found' guards +
  modelConfig tri-state.
Tiny fix: findByCreator badge left-join now also requires enabled=true, so a
disabled role (downgraded to universal by resolveRoleForRequest) no longer shows
a misleading chat-list badge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 14:20:08 +03:00
claude code agent 227
30c3189220 feat(ai-chat): agent roles (admin-defined persona + optional model)
Reusable, workspace-shared agent roles for the built-in AI chat. A role is
a named persona (system-prompt instructions) + optional model override; a
chat is bound to a role at creation and applies it every turn.

Backend:
- migration 20260620T120000: ai_agent_roles table + ai_chats.role_id
  (FK ON DELETE SET NULL); hand-merged types into db.d.ts/entity.types.ts
  (db.d.ts is hand-curated here, full codegen would clobber it).
- core/ai-chat/roles: CRUD module. list = any workspace member; create/
  update/delete = admin (Manage Settings ability, like ai-settings/mcp).
  All repo queries scoped by workspace_id; soft-delete (deleted_at).
- buildSystemPrompt gains roleInstructions: role REPLACES the persona base
  (admin prompt / DEFAULT_PROMPT) but SAFETY_FRAMEWORK + context are always
  still appended.
- stream(): role resolved from ai_chats.role_id for existing chats (never
  the request body -> no per-turn role swap); body.roleId only on creation.
  Disabled (enabled=false) and soft-deleted roles fall back to universal.
- getChatModel(workspaceId, override): role model_config can swap model id /
  driver; a driver without configured creds throws 503 with a clear message
  naming the driver+role, resolved BEFORE response hijack.

Client:
- new-chat role picker (enabled roles only, default Universal assistant),
  roleId sent only on the first message; role badge (emoji+name) in the chat
  header and conversation list; admin Agent-roles management section in
  Settings -> AI (add/edit/delete, MCP-form pattern).

Tests: ai-chat.prompt.spec (role layering + safety always present, incl.
jailbreak); ai.service.spec (override on unconfigured driver -> 503).

Implements docs/ai-agent-roles-plan.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 06:30:06 +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
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
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
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
87d6bdfbd9 feat(ai): redesign AI settings page with per-endpoint test buttons
Rebuild the workspace AI settings page into card-based "Endpoints"
(Chat / Embeddings / Voice) matching the new design, and split the
single connection test into independent per-endpoint Test buttons.

- server: testConnection(workspaceId, capability) probes only the
  requested capability ('chat' | 'embeddings'); add TestAiConnectionDto
  and wire it through the /workspace/ai-settings/test controller
- client: testAiConnection(capability) + capability-typed mutation; two
  independent test mutation instances so Chat/Embeddings results are isolated
- client: full rewrite of ai-provider-settings into Endpoints section —
  drop the provider dropdown (driver is always openai, base URL + key
  always shown), move the "AI chat" and surface the "Semantic search"
  feature toggles into card headers, system message behind an Edit modal,
  pgvector/reindex footer, and a disabled Voice/STT stub
- client: restyle external MCP tools and the MCP server section; collapse
  the AI sections in workspace-settings; remove the standalone
  ai-chat-settings component
- toggles now surface the server error message (e.g. missing pgvector)
- i18n: add new English strings

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 04:20:33 +03:00
vvzvlad
91a63f0b2c fix(ai): stop RAG coverage bar sticking below 100% on empty pages
"Indexed N of M pages" stayed at e.g. "27 of 34" forever even after a
successful full reindex. The numerator counted pages that have embeddings
while the denominator counted ALL non-deleted pages, so empty / text-less
pages (which legitimately store zero embeddings) could never be reached.

Add PageRepo.countEmbeddablePages: counts non-deleted pages that have
non-empty textContent OR already have a stored embedding row, and use it as
the totalPages denominator in AiSettingsService.getMasked. The "has
embeddings" clause covers pages indexed from the content JSON (null
textContent) and guarantees indexedPages <= totalPages. No DB migration.
2026-06-18 03:33:38 +03:00
vvzvlad
80c900eb54 fix(ai): make RAG indexer observable and bound hung embedding calls
The bulk embedding reindex could hang on a single page forever
("Indexed 27 of 34 pages") with zero log output:
- all progress logs were debug-level, suppressed in production (pino info);
- embedMany() had no timeout, so a slow/hung embeddings endpoint blocked
  the sequential per-page loop indefinitely.

Changes:
- ai.service.embedTexts: bound embedMany with AbortSignal.timeout
  (configurable via AI_EMBEDDING_TIMEOUT_MS, default 120000ms); on timeout
  throw a clear, greppable message, classified by both signal.aborted and
  the error name (TimeoutError/AbortError/ResponseAborted) so a real
  provider error racing the timer keeps its diagnostics.
- embedding-indexer.reindexWorkspace: promote lifecycle/progress logs to
  info; log "[i/N] indexing page <id>" BEFORE the await so a hang names the
  stuck page; warn on slow pages (>30s); add timing + final summary.
- .env.example: document AI_EMBEDDING_TIMEOUT_MS.
2026-06-18 03:07:02 +03:00
vvzvlad
b46aed53e3 feat(ai): surface provider error bodies + probe embeddings in test connection
A misconfigured embeddings endpoint failed the RAG indexer with an opaque
"Invalid JSON response" and was not caught by "Test connection" (which only
probed the chat model), so it only surfaced silently during background
indexing.

- add describeProviderError(): formats AI SDK errors as
  "<statusCode>: <message> | response body: <truncated one-line snippet>"
  (statusCode/message/responseBody never carry the API key)
- use it in the bulk-reindex catch and the embedding processor's formatter so
  the real cause (e.g. an HTML 404 from a wrong base URL) is visible in logs
- testConnection now probes chat AND embeddings independently: skips a probe
  when that capability is unconfigured, returns ok:false with a Chat:/Embeddings:
  prefix on real failure, "not configured" when neither is set
2026-06-18 02:35:01 +03:00
vvzvlad
52e19fe678 feat(ai): wire up workspace RAG bulk reindex + manual "Reindex now"
The WORKSPACE_CREATE_EMBEDDINGS / WORKSPACE_DELETE_EMBEDDINGS jobs were
enqueued (on AI Search enable/disable) but had no AI_QUEUE handler, so
existing pages were never indexed ("Indexed 0 of N pages") and disabling
never purged embeddings.

- EmbeddingProcessor: handle WORKSPACE_CREATE_EMBEDDINGS (bulk reindex all
  live pages) and WORKSPACE_DELETE_EMBEDDINGS (purge workspace embeddings)
- EmbeddingIndexerService: add reindexWorkspace() (skips when embeddings
  unconfigured; per-page error isolation) and removeWorkspace()
- PageRepo.getIdsByWorkspace(), PageEmbeddingRepo.deleteByWorkspace()
- AiSettingsService.reindex() + admin-only POST /workspace/ai-settings/reindex
- Frontend: "Reindex now" button, service call and mutation
- Stable per-workspace jobId with remove-before-add so a stale job can't
  block future reindexes; cancel the delayed purge on enable/reindex so it
  can't wipe freshly-built embeddings
2026-06-18 02:15:18 +03:00
vvzvlad
a7f244053b feat(ai): separate base URL and API key for chat vs embedding model
Per-workspace AI provider config previously shared a single base URL and
a single API key between the chat model and the embedding model. Add
dedicated, optional embedding endpoint/token that fall back to the chat
values when empty, preserving backward compatibility.

- db: new migration adds nullable `embedding_api_key_enc` to
  `ai_provider_credentials`; chat key stays in `api_key_enc`
- repo: add `upsertEmbeddingKey` / `clearEmbeddingKey` (on-conflict
  touches only its own column, so chat/embedding keys never overwrite)
- ai-settings.service: store non-secret `embeddingBaseUrl`; resolve()
  applies fallback (embeddingBaseUrl || baseUrl; embedding key || chat
  key); getMasked() exposes raw `embeddingBaseUrl` + `hasEmbeddingApiKey`,
  never the key; update() handles the embedding key write-only
- ai.service: getEmbeddingModel() builds openai/gemini/ollama with the
  embedding-specific URL/key; chat path unchanged
- client: new "Embedding base URL" and "Embedding API key" fields with
  fallback hints and a clear-key action

Requires running the DB migration on deploy.
2026-06-18 01:33:45 +03:00
vvzvlad
1f2d20244e feat(ai-chat): show RAG indexing coverage in AI settings
Display "Indexed N of M pages" on the AI provider settings page so admins
can see how much of the wiki is covered by vector-RAG semantic search.

- page-embedding.repo: add countIndexedPages() — distinct non-deleted pages
  that have stored embeddings in the workspace
- page.repo: add countByWorkspace() — total non-deleted pages
- ai-settings.service: compute both counts in getMasked() (Promise.all) and
  return them with the masked settings; inject PageEmbeddingRepo + PageRepo
- MaskedAiSettings / IAiSettings: add indexedPages + totalPages
- ai-provider-settings: render a dimmed coverage line under "Embedding model"
- i18n: add the "Indexed {{indexed}} of {{total}} pages" key (en-US, ru-RU)
2026-06-17 23:18:51 +03:00
vvzvlad
a4b7919753 fix(ai-chat): OpenAI Chat Completions for multi-turn + provider settings, stream UX & errors" -m "Live-stand fixes (OpenRouter / OpenAI-compatible):
- openai provider: use .chat() (Chat Completions) instead of the default callable
  (Responses API), which gateways reject on multi-turn -> 400.
- updateAiProviderSettings: assemble settings.ai.provider via jsonb_build_object
  with ::text-cast bound params + jsonb_typeof self-heal (postgres.js was
  double-encoding it into an array; the ::text cast avoids 'could not determine
  data type of parameter').
- chat agent: drop the hard maxOutputTokens cap (truncated complex tool calls);
  keep a tiny cap only on the test-connection ping.
- testConnection + chat stream: surface the real provider error (statusCode+message)
  to logs and the UI instead of generic masks; never log the API key.
- chat UI: typing indicator, incremental streaming render, tool 'running' status, Stop.

Also bundled (prior uncommitted ai-chat work):
- history 'AI agent' provenance badge; vector RAG (pgvector image + page_embeddings
  + AI_QUEUE indexer + space-scoped semanticSearch); external MCP servers backend
  (@ai-sdk/mcp client, SSRF IP-pinning, encrypted headers, admin CRUD/Test);
  yjs duplicate-instance fix via pnpm patch (single CJS instance server-side).
2026-06-17 04:28:29 +03:00
vvzvlad
683da7a4c5 feat(ai-chat): per-user AI agent backend — LLM config, read-only agent, provenance schema
WIP checkpoint of the gitmost AI-chat backend (plan stages A + B1 + B3a).
The agent acts under the requesting user's JWT (Docmost CASL enforces page
access); the external service-account /mcp endpoint is untouched.

LLM provider config (A2-A4):
- integrations/crypto: AES-256-GCM SecretBoxService (key derived from APP_SECRET,
  per-record salt/iv; clear error on rotation instead of crashing).
- ai_provider_credentials table/repo/types: encrypted API key stored outside
  workspace settings/baseFields, write-only (never returned by any endpoint).
- integrations/ai: per-workspace AI SDK v6 provider driver (openai/gemini/ollama),
  admin-gated GET(masked)/PATCH(write-only key)/Test endpoints; settings.ai.provider
  holds non-secret config incl. systemPrompt. Removed unused AI_* env getters (DB is
  the single source of truth).

Chat module (A1, A5-A8):
- ai_chats/ai_chat_messages repos (workspace-scoped, soft-delete, tsv never selected).
- core/ai-chat: CRUD + POST /ai-chat/stream (Fastify hijack + AI SDK v6
  pipeUIMessageStreamToResponse, abort on disconnect, persist user/assistant msgs).
- Agent loop: streamText + stepCountIs(8); read tools searchPages/getPage via a
  per-request DocmostClient over loopback REST under the user's minted access token.
- Gate settings.ai.chat (+ 503 when provider unconfigured); buildSystemPrompt with a
  non-removable safety/anti-prompt-injection framework. Per-user rate limit.

Per-user auth (B1):
- @docmost/mcp DocmostClient gains an additive getToken variant (carry a user JWT,
  re-fetch on 401) and exports DocmostClient; the email/password service-account path
  (external /mcp, stdio) is unchanged.

Agent-edit provenance backbone (B3a):
- Migration: pages/page_history (last_updated_source, last_updated_ai_chat_id) and
  comments (created_source, ai_chat_id, resolved_source).
- Signed actor/aiChatId claim in the collab token; onAuthenticate propagates it,
  onStoreDocument writes it with a sticky agent marker, saveHistory copies it.

Migrations auto-run on boot (additive). Write tools, frontend, RAG and external MCP
servers are not in this checkpoint.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 01:36:41 +03:00
vvzvlad
1f5987d6b0 feat(mcp): serve embedded community MCP server at /mcp
Replace the removed enterprise EE MCP (private apps/server/src/ee submodule,
license-gated /mcp route) with our docmost-mcp, vendored as an isolated ESM
workspace package and served by the server over HTTP — no enterprise license.

Backend:
- Add packages/mcp (@docmost/mcp): vendored docmost-mcp refactored into a
  side-effect-free createDocmostMcpServer() factory (38 tools preserved),
  stdio entry kept in stdio.ts, Streamable-HTTP session manager in http.ts.
- Add apps/server McpModule: @Post/@Get/@Delete('mcp') (served at /mcp via the
  existing global-prefix exclude), @SkipTransform + reply.hijack to bridge raw
  Fastify req/res into the SDK transport. The module dynamically imports the
  ESM-only package from CommonJS via a Function-indirected import resolved with
  require.resolve + file:// URL. Gated by the workspace ai.mcp toggle, a
  service-account (MCP_DOCMOST_EMAIL/PASSWORD/API_URL) and optional MCP_TOKEN;
  per-session idle eviction (MCP_SESSION_IDLE_MS).
- Drop the enterprise license check on mcpEnabled in workspace.service.
- Dockerfile: copy packages/mcp into the production image.
- .env.example: document MCP_DOCMOST_*, MCP_TOKEN, MCP_SESSION_IDLE_MS.

Frontend:
- Recreate the community "AI & MCP" workspace-settings panel (mcp-settings.tsx):
  admin-only toggle on settings.ai.mcp with optimistic update, copyable
  ${APP_URL}/mcp URL; wired into workspace-settings page. Reuses existing i18n.

Fixes:
- Pin packages/mcp tiptap deps to 3.20.4 (matching the client) and inline
  getStyleProperty, preventing a duplicate @tiptap/core@3.26.1 from leaking into
  the client editor via pnpm shamefully-hoist (was breaking apps/client tsc).
2026-06-16 23:54:53 +03:00
Philip Okugbe
4295ea09f6 feat(storage): add Azure Blob Storage driver (#2222) 2026-05-21 12:18:58 +01:00
Philipinho
ed0501a864 fix passing wrong object 2026-05-20 19:09:22 +01:00
Philipinho
0d6538ab1a feat: iframe configuration 2026-05-18 22:02:31 +01:00
Philipinho
2b63137217 mail 2026-05-07 18:13:24 +01:00
Philip Okugbe
73dc62bca3 update react-email (#2149) 2026-05-04 22:26:53 +01:00
Philipinho
fcef0c6b96 fix: S3 2026-05-04 20:57:35 +01:00
Philip Okugbe
c247d4c1e3 feat(ee): PDF import (#2142)
* feat: replace pdfjs-dist with firecrawl-pdf-inspector

* use modified firecrawl-pdf-inspector

* feat: pdf import

* increase single file upload size limit

* use npm package

* sync

* update package
2026-05-01 14:56:39 +01:00
Philipinho
a0aea43e25 feat(saml): allow disabling RequestedAuthnContext via env var
Adds SAML_DISABLE_REQUESTED_AUTHN_CONTEXT env var, passed through
    to the SAML strategy's disableRequestedAuthnContext option.
    Defaults to existing behavior (element sent). Set to true to omit
    the element when the IdP authenticates the user with a method that
    does not match (e.g. MFA, FIDO, passwordless), which would
    otherwise cause AADSTS75011 with Microsoft Entra ID.
2026-05-01 11:47:03 +01:00
Philip Okugbe
09c69d7a0f feat: properly preserve table width (#2143) 2026-05-01 00:49:31 +01:00
Philipinho
ec83fc82d5 fix: refactor sanitize 2026-04-27 15:16:26 +01:00
Philipinho
a573acedd0 fix: local storage, and package overrides 2026-04-22 14:13:25 +01:00
Philip Okugbe
a6a7e4370a feat(ee): PDF export api (#2112)
* feat(ee): server side PDF export

* feat: pdf export queue

* sync

* sync
2026-04-14 16:26:54 +01:00
Philip Okugbe
bd68e47e03 feat(ee): page verification workflow (#2102)
* feat: page verification workflow

* feat: refactor page-verification

* sync

* fix type

* fix

* fix

* notification icon

* use full word

* accept .license file

* - update templates
- update migration and notification

* fix copy

* update audit labels

* sync

* add space name
2026-04-13 20:20:34 +01:00
Philip Okugbe
57efb91bd3 feat(ee): ai chat (#2098)
* feat: ai chat

* feat: ai chat

* sync

* cleanup

* view space button
2026-04-10 19:23:47 +01:00
Philip Okugbe
da9b43681e feat: watch space (#2096) 2026-04-09 00:37:51 +01:00
Philip Okugbe
895c1817ae feat: bug fixes (#2084)
* handle enter in inline code

* fix: duplicate comment cache

* track link nodes (backlinks)

* fix en-US translation

* fix internal a-links

* overrides

* 0.71.1
2026-04-05 13:45:36 +01:00
Philip Okugbe
9aaa6c731c feat: add AI_EMBEDDING_SUPPORTS_MRL env var to decouple pgvector dimensions from model API (#2079)
Some embedding models don't accept a `dimensions` parameter. This adds
an optional env var that controls whether the dimension is sent to the
model API, while always using it for pgvector indexing. Preset models
have this handled automatically; the env var allows explicit override
for custom models.
2026-03-31 19:39:49 +01:00
Philip Okugbe
879aa2c3d8 feat: page update notifications (#2074)
* feat: watchers notification and email preferences

* fix: email copy

* digests

* clean up

* fix

* clean up

* move backlinks queue-up to history processor

* fix

* fix keys

* feat: group notifications

* filter

* adjust email digest window
2026-03-31 16:03:59 +01:00
Philip Okugbe
c180d0e487 feat: ratelimits (#2073)
* feat: rate limits

* ip
2026-03-30 15:38:44 +01:00
Philip Okugbe
a062f7a165 fix: enhance confluence importer (#2072)
* fix placeholder

* min resize dimensions

* fix media links

* fix
2026-03-30 13:16:40 +01:00
Philip Okugbe
412962204c fix: editor fixes (#2067)
* autojoiner

* fix marked

* return clipboardTextSerializer as markdown

* fix clipboardTextSerializer for single lines

* cleanup two preceeding spaces in ordered lists item

* fix extra paragraph in task list

* don't zip sinple page exports
2026-03-29 02:19:09 +01:00
Philip Okugbe
7981ef462e feat(editor): audio and PDF nodes (#2064)
* use local resizable

* feat: aduio

* support audio imports

* feat: use confluence real file names

* cleanup

* error handling

* hide notice

* add audio

* fix pulse

* Fix import and export

* unify pulse

* hide in readonly mode

* keywords

* keyword

* translations

* better sort

* feat: PDF embed

* cleanup

* remove audio menu

* open active

* hide focus on readonly mode

* increase iframe default dimension
2026-03-28 17:33:29 +00:00
Philip Okugbe
803f1f0b81 feat: user session management (#2056)
* user session management

* WIP

* cleanup

* license

* cleanup

* don't cache index

* rename current device property

* fix
2026-03-26 20:00:04 +00:00
Philip Okugbe
fa4872e89e fix(deps): package updates (#2041)
* update
* overrides
* override
* fix page update mutation
* fix
* cleanup
* loader
* fix excalidraw package
* override
* fix regex
2026-03-25 10:07:01 +00:00
Philip Okugbe
7520c329d0 fix notion importer (#2027)
* fix notion importer

* fix link selector on mobile
2026-03-15 22:06:40 +00:00
Philip Okugbe
d7a5fda53c feat: better feature flags (#2026)
* feat: feature flag upgrade

* fix translations

* refactor

* fix

* fix
2026-03-15 22:05:32 +00:00
Philip Okugbe
89b94e5d19 feat: refactor link menu (#2025)
* link markview - WIP

* WIP

* feat: refactor links

* cleanup
2026-03-15 17:08:59 +00:00
Philip Okugbe
97c459be67 feat(cloud): add find-workspace and email verification endpoints (#2020)
* feat: add find-workspace and email verification endpoints
* sync
2026-03-14 13:36:30 +00:00
Philip Okugbe
65b89a1b24 fix email button (#2017) 2026-03-14 00:40:32 +00:00
Philip Okugbe
66c26af34b noop audit module (#1994) 2026-03-05 09:29:39 +00:00
Philipinho
057360c6be fix: validate import size 2026-03-03 20:00:05 +00:00
Philipinho
b6478fee84 fix imports 2026-03-03 13:57:10 +00:00
Philipinho
9f4728e279 fix 2026-03-03 00:08:20 +00:00