Commit graph

155 commits

Author SHA1 Message Date
Yun Chan
f505a03d34 fix(ci): point release-metadata checks at the relocated update and publish guards
Some checks are pending
ci / 정본·보안·린트·타입·테스트 (push) Waiting to run
ci / 워크스페이스 빌드 검증 (push) Blocked by required conditions
ci / 모바일 린트·타입·Jest (push) Waiting to run
ci / Supabase Edge Functions + Cloudflare Worker (push) Waiting to run
ci / .NET API 서버 테스트 (push) Waiting to run
deploy-site / deploy (push) Waiting to run
CI run 121 failed at "버전·계약 정본 대조": earlier refactors moved update
gating from UpdateService into update-policy.ts (evaluateUpdateOffer ties
decideUpdate + isWithinRollout), the differential-download switch into
update-adapters.ts, and the Forgejo re-release guard into
lib/immutable-package-guard.mjs (sha256 comparison, abort on conflict).
The checks still looked for the old markers in the old files. They now
verify the same guarantees where the code lives, and the self-test's
negative case targets assertNoImmutableConflicts.

Also clears the lint gate: a control-character regex in Keycap (range now
starts at U+0020) and unused helpers in two red-team tests.
2026-09-28 20:55:11 +09:00
Yun Chan
7b1210b5d1 feat(desktop): honest, unified dictation popups with Esc to cancel
Wave 3 of the 2026-09-28 design overhaul.

- Esc (no modifiers) cancels an in-progress dictation, recording or
  processing: VoiceModeService.requestEscape() queues the dead 'escape'
  action. Clicking the capsule cancels only while recording, so a stray
  click during processing no longer throws the text away.
- Errors show in the user's language: bootstrap now passes error.code to
  the tip (the translated branch never ran), and one core classifier
  (voice-error-message) feeds both the tip and the main-window alert.
- Capsule: bars stay flat without input (no fake "alive" wobble before the
  mic opens), an indeterminate bar replaces the made-up percentage, the
  partial transcript stays visible while processing, an Esc hint, and a
  brief "Inserted" state so success is visible.
- All six popups share popups/_shared (Pretendard, one panel radius,
  selection, kbd hints, reduced motion). History/command popups get
  titles, listbox roles and window sizes from popup-list-geometry (empty
  states were clipped); the command popup's "0" key is registered;
  result popup explains why it appeared and can be closed; captions keep
  a theme-independent dark backing on purpose.
- Popup themes follow the OS when set to system and the document lang
  follows the app language.
- Site hero demo mirrors the new capsule (done state, indeterminate bar,
  Esc hint; 10 locales).
2026-09-28 20:46:24 +09:00
Yun Chan
f064c93197 feat(desktop): information architecture overhaul — home, records, meetings first
Wave 2 of the 2026-09-28 design overhaul ("records are the main
character, a quiet instrument"). Decisions: design.md.

- Shell: sidebar with primary Home / Records / Meetings (Ctrl+1..3) and a
  secondary "Tools" group; every item is a button with aria-current.
  The status bar is removed — its "100% LOCAL PRIVACY · ZERO CLOUD SYNC"
  claim contradicted device sync; the sidebar shows only queried state.
- Navigation context (routes, params, back, useRouteRequest) so Home can
  open a specific meeting or record.
- Home replaces the dashboard: dictation key, start actions, only real
  problems, one-line summary, recent records and meetings, file
  transcription, free-tier limits.
- Settings become a full-screen route with vertical tabs (tabs no longer
  clip) and a shared row grammar; long-standing bugs fixed: team and
  enterprise tiers crashed the license tab (currentTier undefined),
  first-run onboarding could not be left, Pro+ saw "upgrade to Pro+".
- Records: date-grouped single list with in-place expansion, search and
  tags together, mode/favorite filters, load more past 50, undoable
  delete. Meetings: list with search/rename/delete, summary-first detail
  (summary | transcript | documents | my notes), notes persisted locally
  instead of being lost. Tools pages cleaned up; stale-closure example
  chips and a 1550x mouse-distance bug fixed.
- Shared pieces: PageHeader, EmptyStateCard, SearchInput ("/" works),
  ConfirmDialog, notify with undo; display-labels maps domain values to
  typed i18n keys so missing keys fail to compile.
- Mono font stack falls back to Pretendard for Hangul (Korean labels no
  longer render spaced out).
- i18n: new ko/en keys via scripts/i18n-add-keys.mjs (one read-merge-
  write so parallel editors cannot clobber locale files).

Some renderer files also carried pre-existing in-flight changes (error
recovery deep links, meeting document hooks); they are included as-is.
2026-09-28 20:46:08 +09:00
Yun Chan
ba9ef9741e fix: red-team round 3 hardening across desktop, mobile, core and server
Batch of red-team r3 fixes that were in the working tree before the
2026-09-28 design overhaul, committed as one unit with their tests.

- desktop main: STT timeouts and sidecar, voice recording store, sync
  (credentials, audio, knowledge reindex, push gates), runtime
  provisioner, update policy, AltGr keybindings, voice-command policy,
  dictionary file codec/limits, meeting transcript condensing and a
  local recording ledger so interrupted-session recovery only closes
  meetings this device recorded (a phone's live meeting is left alone).
- mobile: login CSRF via implicit token callbacks rejected, account
  deletion/retention, durable queue retention, knowledge realtime
  without unfiltered DELETE, meeting re-record failure paths, cloud STT
  client, preferences store/resync.
- core: text chunking splits long unbroken transcripts to fit, template
  field policy, dictionary limits, meeting markdown inline handling.
- server: payple webhook policy and cancellation order scope, meeting
  document generation quota, team RPC null-role guard, unified LLM
  quota in-flight accounting, knowledge chunk vector index, meeting
  re-record failure paths (migrations 20260929*).
- ci: portable/runtime feed gates, update-policy schema, Forgejo file
  delete and alias planning.

Four older tests are updated to the new contracts rather than the old
behavior: token-pair auth callbacks are rejected, knowledge realtime no
longer subscribes to DELETE, long transcript lines are split, and
meeting recovery requires the local recording ledger for empty rows.
2026-09-28 20:45:52 +09:00
Yun Chan
6533084a0d fix(ollama): stop pull spinner on failure and only activate pulled models 2026-09-28 02:16:25 +09:00
Yun Chan
ed8b585804 fix(history): call memo tag API with params objects, render stored summary, and cancel stale audio playback 2026-09-28 02:16:25 +09:00
Yun Chan
6347115ae7 fix(meeting): stop transcript view toggle from overwriting edits and repair notes enhancement 2026-09-28 02:16:25 +09:00
Yun Chan
1a4c39cb94 fix(keybinding): stop stealing chords on Windows, release mouse holds, keep cleared actions 2026-09-28 02:16:19 +09:00
Yun Chan
115e8488ac fix(templates): route dictation results into active template sessions 2026-09-28 02:16:19 +09:00
Yun Chan
11bf995409 fix(runtime): release engine file holders right before swapping a reinstalled runtime 2026-09-28 02:16:19 +09:00
Yun Chan
88f3dbcd69 fix(update): re-evaluate policy on every update transition and skip NSIS updater on portable installs 2026-09-28 02:16:18 +09:00
Yun Chan
a4acb42573 fix(desktop): align runtime AppUserModelId with electron-builder appId 2026-09-28 02:16:18 +09:00
Yun Chan
5322d981cc fix(security): trust file: URLs only inside the app renderer directory 2026-09-28 02:16:18 +09:00
Yun Chan
2f94d24c99 fix(voice): stop mic-test ref stealing, unblock action queue, keep tail audio, and move context/LLM routing behind ports and pure policies 2026-09-28 02:16:16 +09:00
Yun Chan
2cd462333f fix(stt): gate local fallback on installed engine and extract SidecarSupervisor 2026-09-28 02:16:16 +09:00
Yun Chan
4588b65dfa fix(sidecar): fall back to CPU when the CUDA runtime is unusable 2026-09-28 02:16:15 +09:00
Yun Chan
96b24e279c fix(text-insert): drop nut-js 300ms key delay and send paste as one chord 2026-09-28 02:16:15 +09:00
Yun Chan
9e5b94ced2 fix(suggestion): place overlay in DIPs, size it to content, and harden suggestion sessions 2026-09-28 02:16:15 +09:00
Yun Chan
75e053d72f fix(sync): keep desktop-only language and preset command across settings pulls 2026-09-28 02:16:15 +09:00
Yun Chan
ddc78546f0 fix(desktop): harden session, meeting, caption and LLM lifecycles; route LLM calls through the gateway 2026-09-28 02:16:15 +09:00
Yun Chan
3a46437f28 fix(sync,rag): keep restored rows, reconcile after tombstone pruning, reject partial knowledge docs, surface RAG indexing failures 2026-09-28 02:16:14 +09:00
Yun Chan
f4f9653361 fix(conversation): make realtime sessions cancellable and recoverable 2026-09-28 00:54:03 +09:00
Yun Chan
a85b24d385 fix(meeting): persist transcript segment edits, restore live recording view, and route settings deep links by tab id 2026-09-28 00:54:03 +09:00
Yun Chan
a8983c583a fix(history): require confirmation before clearing all history 2026-09-28 00:54:02 +09:00
Yun Chan
819bc9d789 fix(support): stop faking refund approvals and ticket submissions in the support modal 2026-09-28 00:54:02 +09:00
Yun Chan
06e96c58f4 fix(sound): apply the Sound switch at once instead of after a restart 2026-09-28 00:53:47 +09:00
Yun Chan
91d4b4974c fix(tts): pass Windows TTS text via env and let stop() only cancel its own playback 2026-09-28 00:53:46 +09:00
Yun Chan
9cd81b48c1 refactor(keybinding): extract chord state machine into core and fix AltGr guard 2026-09-28 00:53:46 +09:00
Yun Chan
de1e8a82a4 fix(dictionary): let an edit clear the pronunciation and report duplicate renames 2026-09-28 00:53:46 +09:00
Yun Chan
9d5d043e8b fix(license): keep signed offline keys across restarts and cloud sign-out 2026-09-28 00:53:45 +09:00
Yun Chan
95aa95e986 fix(memo): sanitize the tag used in memo export file names 2026-09-28 00:53:45 +09:00
Yun Chan
524d390bc2 fix(runtime): stage runtime installs strictly, time out only on stalls, and make reinstall actually reinstall 2026-09-28 00:53:44 +09:00
Yun Chan
ba0dbbd813 refactor(instructions): share instruction template rendering via @d3ro/core 2026-09-28 00:53:44 +09:00
Yun Chan
d311e8123f fix(llm): keep system prompts on premium chat, reject incomplete streams, stop double-charging quota 2026-09-28 00:53:44 +09:00
Yun Chan
d96601a283 fix(file-transcription): provision ffmpeg before converting so clean installs stop failing with ENOENT 2026-09-28 00:53:43 +09:00
Yun Chan
c5b3bdedb6 fix(voice): let a click on the RecordingTip actually cancel the recording 2026-09-28 00:53:43 +09:00
Yun Chan
83cf9133ac refactor(desktop): session-scoped voice runtime, STT/LLM ports, caption ownership and meeting export fixes 2026-09-28 00:53:43 +09:00
Yun Chan
0adedf5e7b fix(text-insert): restore every clipboard format after paste and never wipe non-text clipboards 2026-09-28 00:53:42 +09:00
Yun Chan
190b6db284 refactor(suggestion): drive suggestion decisions from one core step and split SuggestionService collaborators 2026-09-28 00:53:42 +09:00
Yun Chan
9aa7302944 fix(sync): bind sync engine to its user DB, scope instructions/templates per account, harden navigation 2026-09-28 00:53:42 +09:00
Yun Chan
1b8fe445f3 fix(suggestion): stop generating mid-typing and let a pause bring a suggestion at once
With a 100 ms trigger left in the installed config, generation started in the
gaps between keystrokes; the next key ended the session silently, and that
session still counted against the 5 s minimum interval, so the moment the
user actually paused nothing came (rate-limited). The log showed a
generate-then-vanish cycle every 5-6 s.

The trigger delay now has a 500 ms floor and settings revision 6 resets a
stored value below it to 600 ms. A session ended because the user kept typing
no longer blocks the next request by the minimum interval (per-minute and
daily caps still apply), and that dismissal is logged.
2026-09-27 22:55:26 +09:00
Yun Chan
7e0c20b510 fix(suggestion): keep a shown suggestion while the user picks it, and suggest in terminals
A candidate list was cleared about two seconds after it appeared: once typing
paused, the not-typing rule dismissed the visible overlay, so a click or
Ctrl+Alt+Enter found nothing to insert. The rule now only stops new
requests; a visible overlay stays until it is accepted, dismissed or goes
stale.

Terminals were excluded from suggestions because the whole screen buffer
reads as the input and the last line is usually a status bar. The input line
is now extracted (Claude Code/Codex >, starship, PowerShell, cmd and POSIX
prompts, wrapped continuation lines) and used as the prefix; no prompt means
no suggestion. Terminals stay out of phrase learning. Accepting with nothing
shown and successful inserts are now logged.
2026-09-27 22:43:22 +09:00
Yun Chan
796916ba92 release: ship v1.9.0 with transcript segments and preset prompts on the phone
Some checks failed
ci / 정본·보안·린트·타입·테스트 (push) Successful in 53s
ci / 모바일 린트·타입·Jest (push) Successful in 45s
ci / Supabase Edge Functions + Cloudflare Worker (push) Successful in 24s
ci / .NET API 서버 테스트 (push) Successful in 17s
deploy-site / deploy (push) Successful in 53s
ci / 워크스페이스 빌드 검증 (push) Successful in 35s
release / release-windows (push) Failing after 9m17s
portable-unsigned / portable-windows (push) Failing after 28m49s
Desktop transcript edits, auto-polish and speaker labels now reach the phone,
which draws meetings from transcript segments, and prompt edits of the four
shared preset commands are used by the phone's commands.

Bumps the product version to 1.9.0 (Android/iOS build 1090000).
2026-09-27 16:24:48 +09:00
Yun Chan
2aac10fc5d feat(desktop): send meeting transcript segments and preset prompt edits to the phone
The phone draws a meeting from its transcript segments before the edited
transcript, so desktop edits, auto-polish and diarization never showed there.
Every desktop transcript change now rebuilds the meeting's segments from its
[MM:SS] [speaker] lines and trims the rest; the line parser moves to
@d3ro/core/meeting-transcript and the meeting view uses it too.

Prompt edits of the four desktop presets that exist on the phone update the
server preset row (a reset restores its default; {{targetLanguage}} is sent
as English, the only target on both sides), and edits made on another desktop
come back. The free-prompt preset has no phone counterpart and stays local.
2026-09-27 16:24:25 +09:00
Yun Chan
3f1fb8eed5 release: ship v1.8.0 with knowledge, recording and settings sync
Some checks failed
ci / 정본·보안·린트·타입·테스트 (push) Successful in 54s
ci / 모바일 린트·타입·Jest (push) Successful in 42s
ci / Supabase Edge Functions + Cloudflare Worker (push) Successful in 22s
ci / .NET API 서버 테스트 (push) Successful in 17s
deploy-site / deploy (push) Successful in 35s
ci / 워크스페이스 빌드 검증 (push) Successful in 37s
release / release-windows (push) Failing after 6m8s
portable-unsigned / portable-windows (push) Failing after 14m8s
Knowledge documents move between desktop, phone and web as source text and
are indexed on each device; desktop recordings upload to the shared storage
and any recording plays from the history card; language, theme, auto-polish
and the active command follow the phone. Document text survives a missing
embedding model so it can be indexed later.

Bumps the product version to 1.8.0 (Android/iOS build 1080000).
2026-09-27 14:45:19 +09:00
Yun Chan
9a8f7e6aa6 feat(desktop): sync knowledge, recordings and shared settings; play any recording
Knowledge documents travel as source-text chunks; each surface embeds them
with its own model, the server index is requested through embed-chunks, and
documents from the phone are stored without a file and indexed from their
chunks. Chunk text is now kept when local embedding fails, so reindexing no
longer needs the original file.

Recordings upload to the mobile storage contract (audio bucket under the
user's folder plus an audio_files row, 50 MiB cap, a Settings > Cloud
toggle) and are removed with their record. The history card gains a play
button that uses the local file or, for phone recordings, a signed URL.

Language (ko/en), system/light/dark theme, auto-polish and the active user
command follow the phone's user_settings with its revision rule; changes that
arrive from the phone reach the open window.
2026-09-27 14:44:56 +09:00
Yun Chan
3d9faedf7d release: ship v1.7.0 with two-way cloud sync and streaming captions
Some checks failed
ci / 정본·보안·린트·타입·테스트 (push) Successful in 52s
ci / 모바일 린트·타입·Jest (push) Successful in 41s
ci / Supabase Edge Functions + Cloudflare Worker (push) Successful in 24s
ci / .NET API 서버 테스트 (push) Successful in 11s
deploy-site / deploy (push) Successful in 44s
ci / 워크스페이스 빌드 검증 (push) Successful in 35s
release / release-windows (push) Failing after 6m37s
portable-unsigned / portable-windows (push) Failing after 21m42s
Signing in now keeps history, dictionary, meetings with memos and documents,
memo tags, user commands and templates the same on desktop, phone and web,
with offline changes queued, deletions shared and phone edits arriving live.
The desktop registers in the phone's device list and signs out when
disconnected there; local-mode records move into the first account.

Live captions stream about a second behind speech, finish lines on pauses,
are polished in context by the local model and can use their own speech
model (the speech engine is downloaded again once, minimum 1.7.0).

Fixes suggestion paste on Ctrl+Alt+Enter, click acceptance, typing detection
in growing chat boxes, one-way cloud sync and failing account exports.

Bumps the product version to 1.7.0 (Android/iOS build 1070000).
2026-09-27 14:08:18 +09:00
Yun Chan
0a4f5aee64 feat(desktop): two-way cloud sync with mobile and web
Rewrites the desktop mirror as services/sync/SyncEngine: a persistent
outbox, per-account server-clock keyset cursors with paging, pulls that never
overwrite unsent local edits, deletions both ways through sync_tombstones and
per-row failure isolation. It now covers history titles and favorites,
dictionary, every meeting's memos and documents, memo tags, user commands and
dictation/meeting templates, and registers the desktop as a device that the
phone can disconnect.

Fixes shipped defects: the first pull after sign-in fetched nothing, only
the first meeting's children were pushed, team meetings leaked into the
personal database and lost team_id on re-push, and Realtime never connected
because Electron's Node 20 has no global WebSocket (ws is now the transport).
Anonymous local-mode records are imported into the first account that signs
in. The settings sync section is translated and shows pending/rejected
changes; synced screens reload on app:dataChanged.
2026-09-27 14:04:49 +09:00
Yun Chan
0d92a4a853 fix(rag): do not mark a document indexed when no chunk could be embedded
Some checks failed
ci / 정본·보안·린트·타입·테스트 (push) Successful in 51s
ci / 모바일 린트·타입·Jest (push) Successful in 37s
ci / Supabase Edge Functions + Cloudflare Worker (push) Successful in 26s
ci / .NET API 서버 테스트 (push) Successful in 14s
deploy-site / deploy (push) Failing after 15s
ci / 워크스페이스 빌드 검증 (push) Failing after 11m7s
With the embedding server unavailable every chunk failed, yet the document
was stored as indexed=true with 0 chunks, so the knowledge base listed it as
searchable while queries could never match it. The red use-case test caught
this; it had been written off as an environment failure.

Now a run with zero embedded chunks leaves indexed=false and throws
RAGEmbeddingFailed (surfaced by reindex, logged by addDocument).

Tests that only hold on the Windows developer machine now declare it: the
bundled SoX binary and PowerShell device discovery run on win32 only, and
the sidecar venv test runs only when sidecar/.venv exists. The Linux Forgejo
runner skips them instead of failing.
2026-09-26 21:10:40 +09:00
Yun Chan
eedd127ea7 refactor(billing): remove Stripe; payments are Payple (web) and Google Play (mobile)
Some checks failed
ci / 정본·보안·린트·타입·테스트 (push) Failing after 1m13s
ci / 워크스페이스 빌드 검증 (push) Has been skipped
ci / 모바일 린트·타입·Jest (push) Failing after 1m4s
ci / Supabase Edge Functions + Cloudflare Worker (push) Successful in 37s
ci / .NET API 서버 테스트 (push) Successful in 27s
deploy-site / deploy (push) Failing after 20s
Stripe is not used. Keeping its checkout, portal and webhook paths meant a
second payment provider, a second return-URL format and dead UI.

- Delete the stripe-checkout, stripe-portal and stripe-webhook functions and
  their config; billing-catalog serves Payple prices only, and the web parser
  rejects a catalog that still mixes in Stripe prices.
- Web: drop the Stripe checkout/portal buttons, provider toggle and return
  notices; billing shows Payple only. Past rows with provider='stripe' are
  still displayed ("Stripe (종료)") with a support contact instead of a portal.
- Desktop: delete the Stripe checkout modal, payment IPC channels, preload
  namespace and their types; "Remove ads with Pro" opens the web billing page
  via license.openBilling. Support/refund copy names Payple.
- billingUrl() loses the Stripe-only success/canceled result option; the
  Deno contract is regenerated.
- Migrations and the DB's accepted provider values are untouched (history).
- Docs and the backlog record the removal (MON-04, EXT-STRIPE-01, GAP-BILL-03).

Verified: typecheck (desktop/web/admin/api-client/mobile), contract:check,
deno check all functions, deno test 80/80, desktop 1478/1480 on the Electron
runtime (2 known environment failures), web and admin builds, release
metadata and mobile boundary self-tests, eslint on changed files.
2026-09-26 20:56:18 +09:00