Composer: replace the no-calendar message (layout shift) with a subtle warning on the NLP chip #1127

Open
opened 2026-10-05 08:36:20 +00:00 by kayg · 9 comments
Owner

Owner report (2026-10-05, Composer)

"Please get rid of this annoying layout-shift message, it is very annoying and interrupts typing. We need to indicate this as an NLP chip, subtly as a warning."

Today, when the Composer detects a plan/event in the typed text but no calendar account is connected, a full-width message ("No calendar account is connected yet, so a plan cannot be saved. Connect a calendar") is inserted above the toolbar. It shifts the layout while the User types.

Wanted: never insert or remove layout while typing. Show the condition on the parsed NLP chip itself (the chip that marks the detected date/time/plan): a subtle warning state (warning-tinted outline or a small warning glyph from tokens). Hover, focus or tap shows a warm tooltip/popover: "No calendar connected · Connect a calendar" (link to the Connected Accounts setting) and "Saved as a Journal entry instead" if that is what happens. Screen readers get the warning in the chip's accessible description, announced politely once. Reserve no space and cause no layout shift (test: Composer height constant while typing a plan phrase with no calendar connected).

## Owner report (2026-10-05, Composer) "Please get rid of this annoying layout-shift message, it is very annoying and interrupts typing. We need to indicate this as an NLP chip, subtly as a warning." Today, when the Composer detects a plan/event in the typed text but no calendar account is connected, a full-width message ("No calendar account is connected yet, so a plan cannot be saved. Connect a calendar") is inserted above the toolbar. It shifts the layout while the User types. Wanted: never insert or remove layout while typing. Show the condition on the parsed NLP chip itself (the chip that marks the detected date/time/plan): a subtle warning state (warning-tinted outline or a small warning glyph from tokens). Hover, focus or tap shows a warm tooltip/popover: "No calendar connected · Connect a calendar" (link to the Connected Accounts setting) and "Saved as a Journal entry instead" if that is what happens. Screen readers get the warning in the chip's accessible description, announced politely once. Reserve no space and cause no layout shift (test: Composer height constant while typing a plan phrase with no calendar connected).
Author
Owner

Started work on job/nlpchip-1127 from dev at base SHA 98584d6f78510a4c72b2d2b9193c46581db04742. I am tracing the existing Journal Composer chip, tooltip and e2e harness before editing.

Started work on `job/nlpchip-1127` from `dev` at base SHA `98584d6f78510a4c72b2d2b9193c46581db04742`. I am tracing the existing Journal Composer chip, tooltip and e2e harness before editing.
Author
Owner

Finding: the conditional <p class="notice"> is the layout-changing no-calendar message. A future Log draft is not saved as a Journal entry: Composer.svelte rejects it at send and keeps the draft. The popover will say it stays in the draft until a calendar is connected. DESIGN §49 I7 names /settings/connected-accounts as the Connected Accounts section link, so the action will use that route.

Finding: the conditional `<p class="notice">` is the layout-changing no-calendar message. A future Log draft is not saved as a Journal entry: `Composer.svelte` rejects it at send and keeps the draft. The popover will say it stays in the draft until a calendar is connected. DESIGN §49 I7 names `/settings/connected-accounts` as the Connected Accounts section link, so the action will use that route.
Author
Owner

Check finding: bun run check rejected the Composer render-loop exception because its scoped syntax hash includes the Svelte file hash. I refreshed the 12 existing #663 pins for the same Composer list identity; the rule count, owners, reasons and expiry are unchanged.

Check finding: `bun run check` rejected the Composer render-loop exception because its scoped syntax hash includes the Svelte file hash. I refreshed the 12 existing #663 pins for the same Composer list identity; the rule count, owners, reasons and expiry are unchanged.
Author
Owner

Additional gate finding: after refreshing Composer pins, bun run check exposed a stale #663 pin for unchanged NotesExplorer.svelte (git diff HEAD -- apps/web/src/lib/notes/NotesExplorer.svelte is empty). I refreshed that pin hash only to the current source fingerprint; its surface, rule count and expiry remain unchanged.

Additional gate finding: after refreshing Composer pins, `bun run check` exposed a stale #663 pin for unchanged `NotesExplorer.svelte` (`git diff HEAD -- apps/web/src/lib/notes/NotesExplorer.svelte` is empty). I refreshed that pin hash only to the current source fingerprint; its surface, rule count and expiry remain unchanged.
Author
Owner

Update: bun run check also stops at a stale #663 pin for unchanged SearchPreview.svelte. I reverted the unrelated NotesExplorer pin refresh and kept the contract-file edits scoped to the Composer surfaces and material rule changed in this issue. The pre-existing non-Composer pin drift remains for the merge round to resolve.

Update: `bun run check` also stops at a stale #663 pin for unchanged `SearchPreview.svelte`. I reverted the unrelated NotesExplorer pin refresh and kept the contract-file edits scoped to the Composer surfaces and material rule changed in this issue. The pre-existing non-Composer pin drift remains for the merge round to resolve.
Author
Owner

Continuing #1127 after the host-load pause. The previous session reported that a future Log draft stays in the Composer until a calendar is connected; its tooltip must describe that actual send behavior. It also found existing non-Composer perf-pin drift that it left untouched. I will merge origin/dev, resume the saved work, and run the requested gates and production screenshots.

Continuing #1127 after the host-load pause. The previous session reported that a future Log draft stays in the Composer until a calendar is connected; its tooltip must describe that actual send behavior. It also found existing non-Composer perf-pin drift that it left untouched. I will merge origin/dev, resume the saved work, and run the requested gates and production screenshots.
Author
Owner

Production e2e evidence for #1127:

  • Future plans with no Calendar stay in the Composer draft. The former full-width notice is absent, and the settled Composer height stays constant while typing.
  • The date chip has a polite accessible description. Desktop keyboard activation and touch activation open the Connected Accounts popover; the touch target is at least 44 px.
  • The build reported macOS platform values (MacIntel, macOS) in every screenshot context.

Screenshots from the real production build:

  • 390 px light: 390 px light Composer warning
  • 390 px dark: 390 px dark Composer warning
  • 820 px light: 820 px light Composer warning
  • 820 px dark: 820 px dark Composer warning
  • 1440 px light: 1440 px light Composer warning
  • 1440 px dark: 1440 px dark Composer warning

Local performance profile (shared build host; load average at start 18.57 / 19.22 / 26.56; docs/perf/baseline.json has no Composer warning profile):

  • Average, 30-char plan, 5 samples: input-to-warning p50 41.8 ms, p95 73.1 ms; server CPU delta 1 tick, server RSS 184176 KiB before/after; driver CPU 115.875 ms, peak RSS 132764 KiB.
  • Worst case, 8000-char plan after 3 rapid edits, 5 samples: p50 221.5 ms, p95 446.4 ms; server CPU delta 3 ticks, RSS 184176 KiB before and 184316 KiB peak/after; driver CPU 249.704 ms, peak RSS 134148 KiB.
Production e2e evidence for #1127: - Future plans with no Calendar stay in the Composer draft. The former full-width notice is absent, and the settled Composer height stays constant while typing. - The date chip has a polite accessible description. Desktop keyboard activation and touch activation open the Connected Accounts popover; the touch target is at least 44 px. - The build reported macOS platform values (`MacIntel`, `macOS`) in every screenshot context. Screenshots from the real production build: - 390 px light: ![390 px light Composer warning](https://git.kayg.org/attachments/c3351f67-8391-4468-b614-96e5daafd922) - 390 px dark: ![390 px dark Composer warning](https://git.kayg.org/attachments/5f0df88b-e2e9-44ae-9fcd-96a2cadd6e35) - 820 px light: ![820 px light Composer warning](https://git.kayg.org/attachments/72f60cb5-f27e-408b-8596-f5cf19fd32ad) - 820 px dark: ![820 px dark Composer warning](https://git.kayg.org/attachments/39219313-08d0-428f-8a46-16590ddb5ca4) - 1440 px light: ![1440 px light Composer warning](https://git.kayg.org/attachments/0e5c810b-7848-47f8-a3d4-999ada854591) - 1440 px dark: ![1440 px dark Composer warning](https://git.kayg.org/attachments/fb0abb46-50cf-47d4-bcab-960457e83be2) Local performance profile (shared build host; load average at start 18.57 / 19.22 / 26.56; `docs/perf/baseline.json` has no Composer warning profile): - Average, 30-char plan, 5 samples: input-to-warning p50 41.8 ms, p95 73.1 ms; server CPU delta 1 tick, server RSS 184176 KiB before/after; driver CPU 115.875 ms, peak RSS 132764 KiB. - Worst case, 8000-char plan after 3 rapid edits, 5 samples: p50 221.5 ms, p95 446.4 ms; server CPU delta 3 ticks, RSS 184176 KiB before and 184316 KiB peak/after; driver CPU 249.704 ms, peak RSS 134148 KiB.
Author
Owner

Post-merge gate finding: after merging origin/dev at 929a2627f518b5ab946da207c5fc11bbdc9040bb, bun run check exits 2 before Svelte checks. perf-lint reports 21,977 exception rows against a 21,964 ratchet and 13 rule counts one over their ceilings. The pre-merge branch at 570e084c8 had 21,977 rows and a 21,977 ratchet; the mismatch came from the incoming origin/dev contracts. The Composer conflict was resolved by refreshing its 49 hashes against the merged source and retaining all 21,928 non-Composer rows from origin/dev. I have not raised the ratchet.

Post-merge gate finding: after merging `origin/dev` at `929a2627f518b5ab946da207c5fc11bbdc9040bb`, `bun run check` exits 2 before Svelte checks. `perf-lint` reports 21,977 exception rows against a 21,964 ratchet and 13 rule counts one over their ceilings. The pre-merge branch at `570e084c8` had 21,977 rows and a 21,977 ratchet; the mismatch came from the incoming `origin/dev` contracts. The Composer conflict was resolved by refreshing its 49 hashes against the merged source and retaining all 21,928 non-Composer rows from `origin/dev`. I have not raised the ratchet.
Author
Owner

#1127 implementation and verification are complete.

What changed

The Composer no longer inserts a full-width no-calendar notice while the User types. An unavailable future plan is marked on its date chip and stays in the draft until a calendar is connected. A past Event explains that it will be saved as a Journal entry. The warning affordance opens an anchored popover with the Connected Accounts link. The warning is described politely for screen readers and the popover does not resize the Composer.

UX gaps closed: stable Composer height while typing and while the popover is open; accessible warning description; keyboard activation and Escape; touch activation with a 44 px target; real Connected Accounts navigation; real production screenshots at 390, 820 and 1440 px in light and dark, with macOS platform emulation.

UX gaps left: no issue-specific interaction gaps found. The overall bun run check remains blocked by the incoming origin/dev perf-lint ratchet mismatch described below.

Files

  • apps/web/src/lib/composer/Composer.svelte
  • apps/web/src/lib/composer/Composer.svelte.test.ts
  • apps/web/e2e/composer.mjs
  • apps/web/package.json
  • bench/composer-warning-1127.mjs
  • contracts/perf/exceptions.json

Production screenshots

  • 390 px, light: 390 light Composer warning
  • 390 px, dark: 390 dark Composer warning
  • 820 px, light: 820 light Composer warning
  • 820 px, dark: 820 dark Composer warning
  • 1440 px, light: 1440 light Composer warning
  • 1440 px, dark: 1440 dark Composer warning

Gates

bun run check exited 2 before Svelte checks. Output:

$ ../../scripts/perf-lint --check && node scripts/check-user-storage.mjs && node scripts/check-glass-tokens.mjs && node scripts/check-type-tokens.mjs && node scripts/check-focus-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
perf-lint: INVALID: exception ratchet: contract.blaze: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.blocked-network: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; contract.dom-bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.model-bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.row-identity: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.snapshot: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; coverage.profile: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; coverage.readiness: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; io.unresolved-call: 16191 exceptions exceed the ratchet ceiling 16190; fix the new violation instead; render.blaze-adapter: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.model_byte_cap: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.model_row_cap: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; total: ledger has 21977 exceptions; ratchet total is 21964

This is from the incoming origin/dev contracts: before the merge the ledger and ratchet both had 21,977 entries; origin/dev has the same ledger and a 21,964 ceiling. I retained the incoming non-Composer entries and did not widen the ratchet. Direct svelte-check completed: svelte-check found 0 errors and 4 warnings in 3 files (existing CSS selector/focus warnings).

Focused Vitest (Composer.svelte.test.ts, after merge):

Test Files  1 passed (1)
     Tests  38 passed (38)
   Start at  13:12:46
   Duration  54.23s (transform 65%, tests 25%, import 5%, environment 4%, setup 2%)

Focused real-production e2e (test:e2e:composer-warning-1127, after merge; profile disabled on this replay):

composer no-Calendar warning e2e: height, keyboard, touch, accessibility and six production screenshots passed; artifacts in .../apps/web/artifacts/nlpchip-1127
CSP REPORTS composer: 0 across 7 pages

Production build completed; final build output:

Compressed 883 static variants; saved 20859703 bytes.

The local performance profile was run once (not in the baseline yet; host load average 18.57 / 19.22 / 26.56). Average 30-character plan, 5 samples: p50 41.8 ms, p95 73.1 ms; server CPU delta 1 tick, RSS 184176 KiB before/after; driver CPU 115.875 ms, peak RSS 132764 KiB. Worst case 8,000-character plan after 3 rapid edits, 5 samples: p50 221.5 ms, p95 446.4 ms; server CPU delta 3 ticks, RSS 184176 KiB before and 184316 KiB peak/after; driver CPU 249.704 ms, peak RSS 134148 KiB.

No Rust source changed, so Rust gates were not applicable. Cleanup output: Removed 8626 files, 7.2GiB total.

Decisions

  • Keep the native date chip’s existing date action. Put the warning details action beside it so the warning does not replace the chip’s control.
  • For future plans, describe the real send behavior: the plan remains in the draft until a calendar is connected. For past Events, describe the existing Journal fallback.
  • Use /settings/connected-accounts, as named in DESIGN §49 I7.

Head SHA: a7d2619041dd3fac12f7a9f0e6837318f662f262.

Known workspace item: the pre-existing untracked owner-composer.png remains untouched and uncommitted. No push, deploy or remote merge was done.

READY FOR MERGE: no — unblock the existing origin/dev perf-lint ratchet mismatch, then rerun bun run check.

#1127 implementation and verification are complete. ## What changed The Composer no longer inserts a full-width no-calendar notice while the User types. An unavailable future plan is marked on its date chip and stays in the draft until a calendar is connected. A past Event explains that it will be saved as a Journal entry. The warning affordance opens an anchored popover with the Connected Accounts link. The warning is described politely for screen readers and the popover does not resize the Composer. UX gaps closed: stable Composer height while typing and while the popover is open; accessible warning description; keyboard activation and Escape; touch activation with a 44 px target; real Connected Accounts navigation; real production screenshots at 390, 820 and 1440 px in light and dark, with macOS platform emulation. UX gaps left: no issue-specific interaction gaps found. The overall `bun run check` remains blocked by the incoming `origin/dev` perf-lint ratchet mismatch described below. ## Files - `apps/web/src/lib/composer/Composer.svelte` - `apps/web/src/lib/composer/Composer.svelte.test.ts` - `apps/web/e2e/composer.mjs` - `apps/web/package.json` - `bench/composer-warning-1127.mjs` - `contracts/perf/exceptions.json` ## Production screenshots - 390 px, light: ![390 light Composer warning](https://git.kayg.org/attachments/8fd1daa6-06a1-4e2b-8022-4c5b73593191) - 390 px, dark: ![390 dark Composer warning](https://git.kayg.org/attachments/74d81649-343c-4ac2-9cbd-c3110c8ca001) - 820 px, light: ![820 light Composer warning](https://git.kayg.org/attachments/488d0cee-8e40-4cae-89ee-d42b12216043) - 820 px, dark: ![820 dark Composer warning](https://git.kayg.org/attachments/cff90679-3915-487b-a221-084fee00089f) - 1440 px, light: ![1440 light Composer warning](https://git.kayg.org/attachments/743a4fc7-e613-4c18-bf08-755f32e4e26f) - 1440 px, dark: ![1440 dark Composer warning](https://git.kayg.org/attachments/212aee3a-657b-4efc-8cd2-5faa9e58bd8b) ## Gates `bun run check` exited 2 before Svelte checks. Output: ```text $ ../../scripts/perf-lint --check && node scripts/check-user-storage.mjs && node scripts/check-glass-tokens.mjs && node scripts/check-type-tokens.mjs && node scripts/check-focus-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json perf-lint: INVALID: exception ratchet: contract.blaze: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.blocked-network: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; contract.dom-bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.model-bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.row-identity: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.snapshot: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; coverage.profile: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; coverage.readiness: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; io.unresolved-call: 16191 exceptions exceed the ratchet ceiling 16190; fix the new violation instead; render.blaze-adapter: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.model_byte_cap: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.model_row_cap: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; total: ledger has 21977 exceptions; ratchet total is 21964 ``` This is from the incoming `origin/dev` contracts: before the merge the ledger and ratchet both had 21,977 entries; `origin/dev` has the same ledger and a 21,964 ceiling. I retained the incoming non-Composer entries and did not widen the ratchet. Direct `svelte-check` completed: `svelte-check found 0 errors and 4 warnings in 3 files` (existing CSS selector/focus warnings). Focused Vitest (`Composer.svelte.test.ts`, after merge): ```text Test Files 1 passed (1) Tests 38 passed (38) Start at 13:12:46 Duration 54.23s (transform 65%, tests 25%, import 5%, environment 4%, setup 2%) ``` Focused real-production e2e (`test:e2e:composer-warning-1127`, after merge; profile disabled on this replay): ```text composer no-Calendar warning e2e: height, keyboard, touch, accessibility and six production screenshots passed; artifacts in .../apps/web/artifacts/nlpchip-1127 CSP REPORTS composer: 0 across 7 pages ``` Production build completed; final build output: ```text Compressed 883 static variants; saved 20859703 bytes. ``` The local performance profile was run once (not in the baseline yet; host load average 18.57 / 19.22 / 26.56). Average 30-character plan, 5 samples: p50 41.8 ms, p95 73.1 ms; server CPU delta 1 tick, RSS 184176 KiB before/after; driver CPU 115.875 ms, peak RSS 132764 KiB. Worst case 8,000-character plan after 3 rapid edits, 5 samples: p50 221.5 ms, p95 446.4 ms; server CPU delta 3 ticks, RSS 184176 KiB before and 184316 KiB peak/after; driver CPU 249.704 ms, peak RSS 134148 KiB. No Rust source changed, so Rust gates were not applicable. Cleanup output: `Removed 8626 files, 7.2GiB total`. ## Decisions - Keep the native date chip’s existing date action. Put the warning details action beside it so the warning does not replace the chip’s control. - For future plans, describe the real send behavior: the plan remains in the draft until a calendar is connected. For past Events, describe the existing Journal fallback. - Use `/settings/connected-accounts`, as named in DESIGN §49 I7. Head SHA: `a7d2619041dd3fac12f7a9f0e6837318f662f262`. Known workspace item: the pre-existing untracked `owner-composer.png` remains untouched and uncommitted. No push, deploy or remote merge was done. READY FOR MERGE: no — unblock the existing `origin/dev` perf-lint ratchet mismatch, then rerun `bun run check`.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#1127
No description provided.