From c90e6bba7b4186a8693d45876544d989c112e633 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 13 Sep 2026 17:15:29 +0000 Subject: [PATCH 1/3] docs(tui): COR-226 session-shared lock_v2 scene Add designer and runtime lock boards for a live read-only /share link at 40x12 and 120x40. The Shared marker is dim status chrome on the header row; /unshare clears it by dropping the live link. Co-authored-by: Mathis --- .../tui-lock-v2/120x40/session-shared.png | Bin 0 -> 36062 bytes .../tui-lock-v2/40x12/session-shared.png | Bin 0 -> 22466 bytes docs/media/tui-lock-v2/SPEC.md | 24 ++- docs/media/tui-lock-v2/index.md | 5 +- .../runtime/120x40/session-shared.png | Bin 0 -> 8927 bytes .../runtime/40x12/session-shared.png | Bin 0 -> 4181 bytes docs/media/tui-lock-v2/runtime/README.md | 7 +- docs/media/tui-lock-v2/tools/boards.py | 23 +++ .../tui-lock-v2/txt/120x40/session-shared.txt | 40 ++++ .../tui-lock-v2/txt/40x12/session-shared.txt | 12 ++ scripts/render-tui-lock-v2.sh | 9 +- src/cortex-tui/src/app/state.rs | 3 + src/cortex-tui/src/lib.rs | 1 + src/cortex-tui/src/lock_v2.rs | 4 +- src/cortex-tui/src/lock_v2_boards.rs | 2 + src/cortex-tui/src/lock_v2_computer.rs | 4 +- src/cortex-tui/src/lock_v2_designed.rs | 4 +- src/cortex-tui/src/lock_v2_ids.rs | 10 +- src/cortex-tui/src/lock_v2_share.rs | 182 ++++++++++++++++++ src/cortex-tui/src/ui/chrome.rs | 23 +++ src/cortex-tui/src/ui/consts.rs | 6 + .../src/views/minimal_session/view.rs | 16 +- 22 files changed, 348 insertions(+), 27 deletions(-) create mode 100644 docs/media/tui-lock-v2/120x40/session-shared.png create mode 100644 docs/media/tui-lock-v2/40x12/session-shared.png create mode 100644 docs/media/tui-lock-v2/runtime/120x40/session-shared.png create mode 100644 docs/media/tui-lock-v2/runtime/40x12/session-shared.png create mode 100644 docs/media/tui-lock-v2/txt/120x40/session-shared.txt create mode 100644 docs/media/tui-lock-v2/txt/40x12/session-shared.txt create mode 100644 src/cortex-tui/src/lock_v2_share.rs diff --git a/docs/media/tui-lock-v2/120x40/session-shared.png b/docs/media/tui-lock-v2/120x40/session-shared.png new file mode 100644 index 0000000000000000000000000000000000000000..61f9cf22e6aa075d94a1d447f27380a3033f2868 GIT binary patch literal 36062 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
  • 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/.txt` — the exact character grid of every board (diff a `MockTerminal` capture against these). - Renderer: `tools/render_lock_v2.py` + `tools/boards.py` (Python 3 + Pillow, IBM Plex Mono @@ -98,7 +98,8 @@ Board geometry used by the renderer (documentation only — the product renders ``` Header-left is intentionally empty on launch. It never paints a shell echo -(`~/…`, `> cortex`). Optional: the session name after `/rename`, dim. +(`~/…`, `> cortex`). Optional, dim: the session name after `/rename`, or the +`Shared · read-only` marker while a `/share` link is live (`Shared` at 40 cols). --- @@ -116,6 +117,13 @@ Legend for recipes: `T` text `#F5F5F5` · `D` dim `#6B7280` · `M` muted `#4B556 - ≥ 90 % of the window: counter turns `A` and the transcript gets one line `Context is 92% full — /compact summarizes the thread to free room.` (`A` + `T` + `D`). +``` + Shared · read-only 14K / 500K ← marker D, left margin col 1 +``` +- Persistent status chrome while a read-only `/share` link is live — `D`, never + accent, never a wash. Clipped before the counter; `Shared` alone at 40 cols. +- `/unshare` clears the marker; the link line stays in the transcript as history. + ### 3.2 User prompt bar ``` @@ -409,6 +417,7 @@ Narrow: no bars, `used / total pct%`. |---|---| | welcome-cortex / welcome-agent / session-empty | `welcome-cortex`, `welcome-agent`, `session-empty` (+ `first-run-tips`) | | session-user-bars / thought / assistant / worked / optin | `session-user-bars`, `session-thought`, `session-thought-expanded`, `session-thinking-live`, `session-assistant`, `session-worked`, `session-optin`, `session-optin-hover` | +| `/share` link live (status-line marker) · `/unshare` clears it | `session-shared` | | composer-empty / typing (+blink) / hover | `composer-empty`, `composer-typing`, `composer-typing-blink`, `composer-hover`, `composer-multiline` | | footer-shortcuts / tokens-topright | `footer-shortcuts`, `footer-hover`, `tokens-topright`, `tokens-topright-warn` | | slash-palette / slash-model-typed | `slash-palette` (`/goal` after `/plan`), `slash-model-typed` | @@ -426,7 +435,8 @@ Narrow: no bars, `used / total pct%`. | local-tools consent · @file chip · undo/redo/rewind | `consent-local-tools`, `composer-file-chip`, `undo-sheet` | Narrow (40×12) set: `welcome-cortex`, `welcome-agent`, `first-run-tips`, `session-empty`, `session-user-bars`, -`session-thinking-live`, `session-assistant`, `session-optin`, `composer-empty`, `composer-typing`, `composer-hover`, +`session-thinking-live`, `session-assistant`, `session-optin`, `session-shared`, `composer-empty`, `composer-typing`, +`composer-hover`, `tokens-topright`, `compact-chat`, `slash-palette`, `goal-chip-active`, `goal-chip-paused`, `goal-chip-done`, `goal-chip-budget`, `goal-chip-blocked`, `slash-model-typed`, `model-list`, `model-effort-high`, `settings-appearance`, `settings-mouse`, `settings-row-hover`, `settings-theme-submenu`, `theme-picker`, `session-fork`, `init-agents`, `custom-commands`, `hooks-lifecycle`, `handoff-confirm`, `mode-plan`, `mode-ask`, diff --git a/docs/media/tui-lock-v2/index.md b/docs/media/tui-lock-v2/index.md index 32cab5c1..720d4f67 100644 --- a/docs/media/tui-lock-v2/index.md +++ b/docs/media/tui-lock-v2/index.md @@ -27,6 +27,7 @@ Regenerate: `python3 tools/render_lock_v2.py --index` (fetches IBM Plex Mono on | `session-worked` | `Worked for Xs` after a reply | [120x40](120x40/session-worked.png) | — | | `session-optin` | `Help improve Cortex` banner — Opt out | Opt in | [120x40](120x40/session-optin.png) | [40x12](40x12/session-optin.png) | | `session-optin-hover` | Banner with the mouse over `[Opt in]` | [120x40](120x40/session-optin-hover.png) | — | +| `session-shared` | `/share` link live — dim `Shared` marker in the status line | [120x40](120x40/session-shared.png) | [40x12](40x12/session-shared.png) | | `composer-empty` | Empty composer — caret before the placeholder, banner green `>` | [120x40](120x40/composer-empty.png) | [40x12](40x12/composer-empty.png) | | `composer-typing` | Mid-type, caret on | [120x40](120x40/composer-typing.png) | [40x12](40x12/composer-typing.png) | | `composer-typing-blink` | Mid-type, caret off (blink phase) | [120x40](120x40/composer-typing-blink.png) | — | @@ -126,4 +127,6 @@ Regenerate: `python3 tools/render_lock_v2.py --index` (fetches IBM Plex Mono on | `composer-file-chip` | Composer with attached @file chip in prompt | [120x40](120x40/composer-file-chip.png) | [40x12](40x12/composer-file-chip.png) | | `undo-sheet` | /undo /redo /rewind sheet | [120x40](120x40/undo-sheet.png) | [40x12](40x12/undo-sheet.png) | -**95** runtime boards at 120x40 · **49** at 40x12. Designer PNG pack from other PRs is unchanged; new COR-18/225/227/228 scenes are runtime captures. Computer lock boards from #69 stay in the pack. +**96** runtime boards at 120x40 · **50** at 40x12. Designer PNG pack from other PRs is unchanged; new COR-18/225/227/228 scenes are runtime captures. Computer lock boards from #69 stay in the pack. `session-shared` (COR-226) is at both sizes. + +Runtime MockTerminal pack (same ids): [`runtime/120x40`](runtime/120x40/) (96) · [`runtime/40x12`](runtime/40x12/) (50). diff --git a/docs/media/tui-lock-v2/runtime/120x40/session-shared.png b/docs/media/tui-lock-v2/runtime/120x40/session-shared.png new file mode 100644 index 0000000000000000000000000000000000000000..079eec2c23304e3eefccbb676b445df48ef10f02 GIT binary patch literal 8927 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 literal 0 HcmV?d00001 diff --git a/docs/media/tui-lock-v2/runtime/40x12/session-shared.png b/docs/media/tui-lock-v2/runtime/40x12/session-shared.png new file mode 100644 index 0000000000000000000000000000000000000000..d4f572157849ee71b4e73178e6ce86920225620a GIT binary patch literal 4181 zcmZ`+cTf}9_NGV^0!RrhRB0kbK%^H%P!<7~4xzUdk(R84-lPcvA__rDPy{JLT$BU^ z5?n$;$CVNw0i=Z$Na!^Xe!MsHn>X`j-SXAFWan zu^-0tt!brrjhf_@BPwAfJEH zb&A?BnRJmU$!>e;IPoKzXGMX_LD#iPC4VLWb$Fv*c)jxFXLATuvvs+*^LuJNPl-mm z7aQk=(TS!{W>4y+6m#cJKExHrET(Gqz&0YYG+~AdQO)_FGOt)Z+M|8d%>|3@&7p-J zaGw*gmgY&>ltz0rG3$MJ5z)Iuz81X1xd-9+5e{Q$pSctHng8*s{sUGv*#$PxPY>n> zoH?Jb=bJKIbcw?$_KT0e0P7&Md@6H*M#bs)? zMg3Mrg8fkz@*NQ}p=>w;fe<<2e67ZaQQctlEqN2t9k>FNbkP1-49pxvcNLUK+2U&w z%NND6p9M%uOXs|=?Yc4D&u`e%P0*Vh&$&PEc8iW`=jJMDsd@T84BLW^k~eZ}hw zkT(0m9_3c`wMeb?L+@+)rydRCB&0PsEFe9_AV;GZgb?i4D`kGatMh)7niiFlf8OaU zXhG3{V-m50=;avSmW$LVY-aH@^Xd4gN3JLpS2uL&KrMu-w^-_*cDSyz)}OE%8sR<0 zDNc>nZozJH3x>~>I5cW zTfZa(bdw)FEq)<8TwbSD`l5||LpR3Od%V5e{$oZO+xV<~95ve_Xt?gvkogZ+fuw=K z@{j-_uE0QJI3PliMXte$@Bd$#5j+8M6uyvEhh3G+HuNi?#$LgSj_Baf^JHN1K z^<)|dL&^orll3XQA4TRx1;Ek z!O@wT_t(w$^yj9sR0X`DQw@|jK&ZI1y|8pJWnG^Q_NHZsbN$i0`bDg^9{E|H%ly7~ z`fm}>71zZ5TIafv8dAlQ@$ahXE76zcJKuPL`eI~vrf1=!3h(AIuh+gTIXAk`5vw<- zIARA1`GQoopZIet*+;z)5T4WP(E1FC+6~D%_1Dlx+YPL;q9+B|b)Zk?+NK2325679 zMuMFVnRvs~<<<<*D;J{PJ1u6Aj0)(M_neB~-B40Z2L*=Jk8uM*1BY{N3sQg=ke^a~#5CLKumeemHDc2Z-i-xXL06qNn2ivfAmiU|H)wZ+xM6-=wj*ztR zwcRcVYd*@!J**(GCC0EiT0!DNP&9(vtdNs)b(Y>msD{dGs8 zANttq>O9XBH|g#xQ!?ktugpcJSa^qM{P_9v3S<~znq)bAbi5eL9dVvD@-=XH{n&xf zLvY}53rJ#w&tVeI7{<**W;KWW1s}r9p5#`P zN9dZFmNQokp}CdNY^_~`l-ETLCIMxGzEbU6Mi!!n*3mDr*g2ZN_Mo%*g2E%Lm3@#1 zBi6mKJQae376&e%fCTzv7{H1@%IuJj$~kdLclU&+B_KajWfskB&`t6qR=oR3~)tkVr~%ZE3@ zLmQ24rVL~l2Xkj)@)?2M3)C8LfsILt$k^mS_;?0ZTC$CNE}Sd~_tfHq6vmAHu0C0& z^iEl#r`Q`tPodjth(i6K!7sQC7xKDx0Hy~Q`_uRRkNqa}nkpvU&{WjM%j|FITrfsI z6!|2AuCoezG;KK;3~l{zUoRLfyYt6eUj-;1*wNh?Qh!&mYOHEAQ{6spylh=3Au}7DZsIPnRMVa}$Ny zPwfKL(nfYSu^jDTNV95!RSI_=@E~fWv+B`^1t}D|9 zDSHf&`lfa6bNov_5yC3D_Dljll?amUi)A4y`q+_~HFW0+m=>8N-ulyD_fg$yH=6Wb z8|p0tr|ZNH>#{=sx^pi9o+8tl48T}Di8aCVsGKC?I%PQOANsfLlGnwbOh*PzSG5E zcHgp}Rm*F_XVW#_+P?Wx~7ynVVn?K?g>#+8PP6z$_#+ zIcWyM{%WLnWVyAoX(&v<{%~rDr|(ng2OA}?UfLhjWaB;t7EIY}8~)MFI$qX43NjEb z7mWU$qP#|59wv*^~v#GL%H(2`i|#=l~m6s-S&S+^KK1B zb?izVInI<52y%#mk_P!RZofF;xdNW8r3HV#DTHepf@j0~S$;>7J7qVLjJ?_T3+Y2l4N{*fyuo}Kl4%fqLdXOUPa_?6Q*3BzV*dxwmh?cg&sv{8>r zg;wxfii*N@U5d(^3uOH-#{I`FVebTzkl03>x%i-mDEH#e`r$4(VI;*G%f zmgkQ}u6j6G4T^_W=7gNfucH<=gCqMU!~yk>;l{P>T@rqe72cG zg*o-{hf7IGeb0N_i+kgk_=ied=hZ%NL=%d1meqK@!OyJYLVj(ZugiArLyripFAA9* z@C-Kg7lnl^A{$?Bev7VP94f~jX?WcD?!&FVzeA?jin?WT{4DtN7#Qb&&Ab)CDp6Z% z03SR1ZE#J!yX7KS_2w0kT5G%nG842evs;0yoSb|ezw<>8G#|9WlHMu#=?8M`@i6@OCF>$G^z<5vC9f-7 zg?rv;wgmUL9Z7l12T3gNo*agCw`D#*Cxq&;FLe+S%noP>tn&R)6uYffIasPxVs*z5 zeC=8d?JTevK{xHSTFZ?Ub2jzKP11XbBA@Jag44BeiM@CAiYQUXhf0D3>k&{J0r_j^ zPoP;F5~C1r%HEv+)w!;ZYZ5L95X z`"), ("composer-typing", board_composer_typing, True, "B", "Mid-type, caret on"), ("composer-typing-blink", board_composer_typing_blink, False, "B", "Mid-type, caret off (blink phase)"), diff --git a/docs/media/tui-lock-v2/txt/120x40/session-shared.txt b/docs/media/tui-lock-v2/txt/120x40/session-shared.txt new file mode 100644 index 00000000..503d8b4c --- /dev/null +++ b/docs/media/tui-lock-v2/txt/120x40/session-shared.txt @@ -0,0 +1,40 @@ + Shared · read-only 14K / 500K + + > /share 04:41 PM + + Shared this session — read-only link is live. + cortex.foundation/share/9f4c2a71 · copied to your clipboard + Anyone with the link can read this session. /unshare turns it off. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ╭─ Agent ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ + │ > Plan, search, build anything │ + ╰─────────────────────────────────────────────────────────────────────────────────────────── Cortex Mini 1 (medium) ─╯ + + Shift+Tab:mode | Ctrl+x:shortcuts diff --git a/docs/media/tui-lock-v2/txt/40x12/session-shared.txt b/docs/media/tui-lock-v2/txt/40x12/session-shared.txt new file mode 100644 index 00000000..48d0e1d4 --- /dev/null +++ b/docs/media/tui-lock-v2/txt/40x12/session-shared.txt @@ -0,0 +1,12 @@ + Shared 14K / 500K + + > /share + + Shared — read-only link is live. + cortex.foundation/share/9f4c2a71 + + ╭─ Agent ────────────────────────────╮ + │ > Plan, search, build anything │ + ╰─────────── Cortex Mini 1 (medium) ─╯ + + Shift+Tab:mode | Ctrl+x:shortcuts diff --git a/scripts/render-tui-lock-v2.sh b/scripts/render-tui-lock-v2.sh index 552319d6..9720d369 100755 --- a/scripts/render-tui-lock-v2.sh +++ b/scripts/render-tui-lock-v2.sh @@ -59,8 +59,8 @@ for spec in 40x12 120x40; do python3 scripts/ansi-frames-to-gif.py --frames "$frames" --png-only "$pngs" done -unique_pngs "$output_dir/40x12" 49 -unique_pngs "$output_dir/120x40" 95 +unique_pngs "$output_dir/40x12" 50 +unique_pngs "$output_dir/120x40" 96 python3 - "$output_dir" <<'PY' from pathlib import Path @@ -100,12 +100,13 @@ is banner green `#1F4945`; historical violet `#A78BFA` is not the lock. Designer boards (pixel target) live in `docs/media/tui-lock-v2/{40x12,120x40}/`. These runtime frames are what Designer cli signs off against. -SPEC §7: **95** boards at 120×40 and **49** at 40×12. Each filename is one +SPEC §7: **96** boards at 120×40 and **50** at 40×12. Each filename is one distinct live state — no two PNGs share a sha256. Includes `/goal` composer chips (`goal-chip-*`), distinct `offline` and `rate-limit` diagnostics, Computer lock boards (`computer-disconnected`, `computer-cloud-default`), local-tools consent, composer `@file` chip, `/undo` `/redo` `/rewind` sheet, -and runtime-only COR-18/225/227/228 scenes. +runtime-only COR-18/225/227/228 scenes, +and the `session-shared` status-line marker while a read-only `/share` link is live. EOF diff --git a/src/cortex-tui/src/app/state.rs b/src/cortex-tui/src/app/state.rs index 6109dd4c..328422eb 100644 --- a/src/cortex-tui/src/app/state.rs +++ b/src/cortex-tui/src/app/state.rs @@ -210,6 +210,8 @@ pub struct AppState { pub computer_held: bool, /// Welcome info card should lock-paint Computer · Cloud as the shipped default. pub show_computer_default: bool, + /// Live read-only share link minted by `/share`; `None` once `/unshare` clears it. + pub share_link: Option, /// Launched via the `agent` binary / alias. pub agent_entrypoint: bool, /// Token counter used / window. @@ -354,6 +356,7 @@ impl AppState { rate_limit_held: false, computer_held: false, show_computer_default: false, + share_link: None, agent_entrypoint: false, tokens_used: 0, context_window: 500_000, diff --git a/src/cortex-tui/src/lib.rs b/src/cortex-tui/src/lib.rs index af0ce279..3e94a5c6 100644 --- a/src/cortex-tui/src/lib.rs +++ b/src/cortex-tui/src/lib.rs @@ -112,6 +112,7 @@ mod lock_v2_ids; mod lock_v2_network; mod lock_v2_parity; mod lock_v2_scenes; +mod lock_v2_share; pub mod readme_hero; pub mod readme_hero_boards; pub mod splash_chrome; diff --git a/src/cortex-tui/src/lock_v2.rs b/src/cortex-tui/src/lock_v2.rs index 2cec5e16..5fcaf074 100644 --- a/src/cortex-tui/src/lock_v2.rs +++ b/src/cortex-tui/src/lock_v2.rs @@ -158,8 +158,8 @@ mod tests { #[test] fn lock_v2_wide_count_is_spec() { - assert_eq!(LOCK_V2_WIDE_IDS.len(), 95); - assert_eq!(LOCK_V2_NARROW_IDS.len(), 49); + assert_eq!(LOCK_V2_WIDE_IDS.len(), 96); + assert_eq!(LOCK_V2_NARROW_IDS.len(), 50); } #[test] diff --git a/src/cortex-tui/src/lock_v2_boards.rs b/src/cortex-tui/src/lock_v2_boards.rs index d2ddf81b..61ae180c 100644 --- a/src/cortex-tui/src/lock_v2_boards.rs +++ b/src/cortex-tui/src/lock_v2_boards.rs @@ -21,6 +21,7 @@ use crate::lock_v2_goal::{ use crate::lock_v2_network::apply_offline_rate_limit_scene; use crate::lock_v2_parity::apply_parity_scene; use crate::lock_v2_scenes::*; +use crate::lock_v2_share::apply_share_scene; use crate::modal::mcp_manager::{McpServerInfo, McpStatus}; use crate::session::SessionSummary; use crate::ui::consts::SERVICE_UNAVAILABLE_NEXT_STEP; @@ -814,6 +815,7 @@ Tell me what you'd like to do.", id if apply_offline_rate_limit_scene(id, &mut state) => {} id if apply_parity_scene(id, &mut state, width) => {} id if apply_designed_scene(id, &mut state, width) => {} + id if apply_share_scene(id, &mut state) => {} id if apply_goal_chip_scene(id, &mut state) => {} "computer-disconnected" | "computer-cloud-default" => { assert!( diff --git a/src/cortex-tui/src/lock_v2_computer.rs b/src/cortex-tui/src/lock_v2_computer.rs index 9dfe1019..8c251b68 100644 --- a/src/cortex-tui/src/lock_v2_computer.rs +++ b/src/cortex-tui/src/lock_v2_computer.rs @@ -115,8 +115,8 @@ mod tests { assert!(LOCK_V2_WIDE_IDS.contains(id), "{id} missing from wide"); assert!(LOCK_V2_NARROW_IDS.contains(id), "{id} missing from narrow"); } - assert_eq!(LOCK_V2_WIDE_IDS.len(), 95); - assert_eq!(LOCK_V2_NARROW_IDS.len(), 49); + assert_eq!(LOCK_V2_WIDE_IDS.len(), 96); + assert_eq!(LOCK_V2_NARROW_IDS.len(), 50); let mut seen = std::collections::HashSet::new(); for id in LOCK_V2_WIDE_IDS.iter().chain(LOCK_V2_NARROW_IDS) { seen.insert(*id); diff --git a/src/cortex-tui/src/lock_v2_designed.rs b/src/cortex-tui/src/lock_v2_designed.rs index 35f0fc15..8a20698d 100644 --- a/src/cortex-tui/src/lock_v2_designed.rs +++ b/src/cortex-tui/src/lock_v2_designed.rs @@ -246,8 +246,8 @@ mod tests { } assert!(!LOCK_V2_WIDE_IDS.contains(&"share-link")); assert!(!LOCK_V2_WIDE_IDS.contains(&"unshare")); - assert_eq!(LOCK_V2_WIDE_IDS.len(), 95); - assert_eq!(LOCK_V2_NARROW_IDS.len(), 49); + assert_eq!(LOCK_V2_WIDE_IDS.len(), 96); + assert_eq!(LOCK_V2_NARROW_IDS.len(), 50); } #[test] diff --git a/src/cortex-tui/src/lock_v2_ids.rs b/src/cortex-tui/src/lock_v2_ids.rs index beb017d3..a59cb585 100644 --- a/src/cortex-tui/src/lock_v2_ids.rs +++ b/src/cortex-tui/src/lock_v2_ids.rs @@ -1,7 +1,7 @@ //! Lock v2 scene id lists. Split out of [`crate::lock_v2`] so adding `/goal` //! boards does not grow that file past the source-policy line-count baseline. -/// Narrow (40×12) SPEC §7 set — 49 boards. +/// Narrow (40×12) SPEC §7 set — 50 boards. pub const LOCK_V2_NARROW_IDS: &[&str] = &[ "welcome-cortex", "welcome-agent", @@ -11,6 +11,7 @@ pub const LOCK_V2_NARROW_IDS: &[&str] = &[ "session-thinking-live", "session-assistant", "session-optin", + "session-shared", "composer-empty", "composer-typing", "composer-hover", @@ -54,7 +55,7 @@ pub const LOCK_V2_NARROW_IDS: &[&str] = &[ "undo-sheet", ]; -/// Wide (120×40) SPEC §7 set — 95 boards. +/// Wide (120×40) SPEC §7 set — 96 boards. pub const LOCK_V2_WIDE_IDS: &[&str] = &[ "welcome-cortex", "welcome-agent", @@ -68,6 +69,7 @@ pub const LOCK_V2_WIDE_IDS: &[&str] = &[ "session-worked", "session-optin", "session-optin-hover", + "session-shared", "composer-empty", "composer-typing", "composer-typing-blink", @@ -169,8 +171,8 @@ mod tests { #[test] fn lock_v2_id_counts_and_unique() { - assert_eq!(LOCK_V2_WIDE_IDS.len(), 95); - assert_eq!(LOCK_V2_NARROW_IDS.len(), 49); + assert_eq!(LOCK_V2_WIDE_IDS.len(), 96); + assert_eq!(LOCK_V2_NARROW_IDS.len(), 50); let mut wide = HashSet::new(); for id in LOCK_V2_WIDE_IDS { assert!(wide.insert(*id), "duplicate wide id {id}"); diff --git a/src/cortex-tui/src/lock_v2_share.rs b/src/cortex-tui/src/lock_v2_share.rs new file mode 100644 index 00000000..6cd013c1 --- /dev/null +++ b/src/cortex-tui/src/lock_v2_share.rs @@ -0,0 +1,182 @@ +//! `/share` lock v2 scene — read-only share-link chrome. +//! +//! Split out of [`crate::lock_v2`] so the source-policy line-count baseline on +//! that file does not regress. The `Shared` marker is persistent status chrome: +//! it stays up for as long as the link is live, and `/unshare` clears it by +//! dropping [`crate::app::AppState::share_link`] back to `None` — idle chrome, +//! no confirmation sheet. + +use cortex_core::widgets::Message; + +use crate::app::AppState; + +/// Read-only link minted by `/share`, as it appears in the transcript. +pub const SHARE_URL: &str = "cortex.foundation/share/9f4c2a71"; +pub const SHARE_TITLE: &str = "Shared this session — read-only link is live."; +pub const SHARE_TITLE_NARROW: &str = "Shared — read-only link is live."; +pub const SHARE_LINK_LINE: &str = "cortex.foundation/share/9f4c2a71 · copied to your clipboard"; +pub const SHARE_SCOPE: &str = "Anyone with the link can read this session. /unshare turns it off."; + +/// Apply the `session-shared` lock scene. Returns `false` for any other `id`. +pub fn apply_share_scene(id: &str, state: &mut AppState) -> bool { + if id != "session-shared" { + return false; + } + let narrow = state.terminal_size.0 <= 40; + state.show_launch_splash = false; + state.tokens_used = 14_000; + state.share_link = Some(SHARE_URL.to_string()); + state.add_message(Message::user("/share").with_timestamp("04:41 PM")); + state.add_message(Message::system(if narrow { + SHARE_TITLE_NARROW + } else { + SHARE_TITLE + })); + state.add_message(Message::system(if narrow { + SHARE_URL + } else { + SHARE_LINK_LINE + })); + if !narrow { + state.add_message(Message::system(SHARE_SCOPE)); + } + true +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lock_v2::render_lock_v2_scene; + use crate::ui::consts::{SHARE_MARKER, SHARE_MARKER_NARROW}; + use crate::views::minimal_session::PLACEHOLDER_IDLE; + use cortex_core::style::{ERROR, TEXT_DIM}; + use ratatui::style::Color; + + fn row_text(frame: &crate::lock_proof::LockFrame, y: u16) -> String { + (0..frame.buffer.area.width) + .map(|x| frame.buffer[(x, y)].symbol()) + .collect() + } + + fn first_fg(frame: &crate::lock_proof::LockFrame, needle: &str) -> Option { + for y in 0..frame.buffer.area.height { + let row = row_text(frame, y); + if let Some(idx) = row.find(needle) { + return Some(frame.buffer[(idx as u16, y)].fg); + } + } + None + } + + #[test] + fn share_marker_is_dim_status_chrome_at_both_sizes() { + for (width, height, marker) in + [(120u16, 40u16, SHARE_MARKER), (40, 12, SHARE_MARKER_NARROW)] + { + let frame = render_lock_v2_scene("session-shared", width, height) + .unwrap_or_else(|e| panic!("session-shared {width}x{height}: {e}")); + let header = row_text(&frame, 0); + assert!( + header.contains(marker), + "share marker on the status line at {width}x{height}:\n{header}" + ); + assert!( + header.contains("Shared"), + "narrow must keep the word Shared at {width}x{height}:\n{header}" + ); + assert!( + header.contains("14K / 500K"), + "token counter stays on the status line at {width}x{height}:\n{header}" + ); + assert_eq!( + first_fg(&frame, "Shared"), + Some(TEXT_DIM), + "share marker is dim at {width}x{height}" + ); + assert!( + frame.plain.contains(SHARE_URL), + "minted read-only link at {width}x{height}:\n{}", + frame.plain + ); + } + } + + #[test] + fn shared_session_stays_interactive_with_the_idle_footer() { + let frame = render_lock_v2_scene("session-shared", 120, 40).expect("wide"); + assert!( + frame.plain.contains(PLACEHOLDER_IDLE), + "composer stays interactive — not held:\n{}", + frame.plain + ); + assert!( + frame.plain.contains("Shift+Tab:mode") && frame.plain.contains("Ctrl+x:shortcuts"), + "normal session footer:\n{}", + frame.plain + ); + assert!( + !frame.plain.contains("Choose an option above"), + "share chrome is not an approval sheet:\n{}", + frame.plain + ); + assert!( + frame.plain.contains("copied to your clipboard"), + "wide board notes the clipboard copy:\n{}", + frame.plain + ); + assert!( + frame.plain.contains("/unshare"), + "wide board names /unshare as the way out:\n{}", + frame.plain + ); + assert_ne!( + first_fg(&frame, "Shared this session"), + Some(ERROR), + "share copy is not an error" + ); + } + + #[test] + fn session_shared_is_distinct_from_neighbouring_boards() { + let shared_wide = render_lock_v2_scene("session-shared", 120, 40).expect("wide"); + let shared_narrow = render_lock_v2_scene("session-shared", 40, 12).expect("narrow"); + assert_ne!(shared_wide.ansi, shared_narrow.ansi); + for other in [ + "session-empty", + "session-user-bars", + "cloud-handoff", + "goal-chip-active", + ] { + let frame = render_lock_v2_scene(other, 120, 40).expect(other); + assert_ne!( + shared_wide.ansi, frame.ansi, + "session-shared must differ from {other}" + ); + assert!( + !row_text(&frame, 0).contains("Shared"), + "{other} must not paint the share marker:\n{}", + row_text(&frame, 0) + ); + } + } + + #[test] + fn unshare_clears_the_marker() { + let mut state = AppState::default(); + state.terminal_size = (120, 40); + assert!(apply_share_scene("session-shared", &mut state)); + assert_eq!(state.share_link.as_deref(), Some(SHARE_URL)); + // `/unshare` is idle chrome: dropping the link is all it takes. + state.share_link = None; + assert!(state.share_link.is_none()); + } + + #[test] + fn apply_rejects_unknown_ids() { + let mut state = AppState::default(); + assert!(!apply_share_scene("session-empty", &mut state)); + assert!(!apply_share_scene("cloud-handoff", &mut state)); + assert!(state.share_link.is_none()); + assert!(state.messages.is_empty()); + } +} diff --git a/src/cortex-tui/src/ui/chrome.rs b/src/cortex-tui/src/ui/chrome.rs index 82f7b84e..7e1338fb 100644 --- a/src/cortex-tui/src/ui/chrome.rs +++ b/src/cortex-tui/src/ui/chrome.rs @@ -74,6 +74,29 @@ pub fn paint_token_counter( buf.set_string(x, y, &text, style); } +/// Dim status marker on the header row, left margin, clipped so it never runs +/// into the token counter. Status chrome only — never accent. +pub fn paint_status_marker(area: Rect, y: u16, buf: &mut Buffer, label: &str, reserved: u16) { + if area.width == 0 || y >= area.bottom() || label.is_empty() { + return; + } + let gutter = content_gutter(false, area.width); + let room = area + .width + .saturating_sub(gutter) + .saturating_sub(reserved.saturating_add(2)); + if room == 0 { + return; + } + let text: String = label.chars().take(room as usize).collect(); + buf.set_string( + area.x.saturating_add(gutter), + y, + text, + Style::default().fg(TEXT_DIM), + ); +} + /// 12-hour `h:mm AM` clock. pub fn format_clock_12h(hour: u32, minute: u32) -> String { use chrono::Timelike; diff --git a/src/cortex-tui/src/ui/consts.rs b/src/cortex-tui/src/ui/consts.rs index 3d98eab0..e0695595 100644 --- a/src/cortex-tui/src/ui/consts.rs +++ b/src/cortex-tui/src/ui/consts.rs @@ -66,6 +66,12 @@ pub const PLACEHOLDER_DISCONNECTED: &str = "Connect a host — or unset CORTEX_C /// Narrow (40-column) form of the disconnected placeholder. pub const PLACEHOLDER_DISCONNECTED_NARROW: &str = "Connect a host — or unset for Cloud"; +/// Status-line marker shown while a read-only `/share` link is live. Dim status +/// chrome, never accent — `/unshare` clears it. +pub const SHARE_MARKER: &str = "Shared · read-only"; +/// Narrow (40-column) form of the share marker. Keeps the word `Shared`. +pub const SHARE_MARKER_NARROW: &str = "Shared"; + /// Sandbox deny title painted in error red. pub const SANDBOX_DENIED_TITLE: &str = "Sandbox denied"; diff --git a/src/cortex-tui/src/views/minimal_session/view.rs b/src/cortex-tui/src/views/minimal_session/view.rs index 1fa5695f..03bcf703 100644 --- a/src/cortex-tui/src/views/minimal_session/view.rs +++ b/src/cortex-tui/src/views/minimal_session/view.rs @@ -12,8 +12,8 @@ use super::rendering::{ use crate::app::AppState; use crate::commands::PALETTE_HOME_LIMIT; use crate::ui::chrome::{ - FooterSet, composer_caret_style, composer_inner, fill_inky, model_chip, paint_composer_box, - paint_footer, paint_token_counter, + FooterSet, composer_caret_style, composer_inner, fill_inky, format_token_counter, model_chip, + paint_composer_box, paint_footer, paint_status_marker, paint_token_counter, }; use crate::ui::colors::AdaptiveColors; use cortex_core::style::{ACCENT, TEXT, TEXT_BRIGHT, TEXT_DIM}; @@ -605,6 +605,18 @@ impl<'a> Widget for MinimalSessionView<'a> { self.app_state.context_window, warn_tokens, ); + if self.app_state.share_link.is_some() { + let label = if area_is_narrow(area.width) { + crate::ui::consts::SHARE_MARKER_NARROW + } else { + crate::ui::consts::SHARE_MARKER + }; + let counter_cols = + format_token_counter(self.app_state.tokens_used, self.app_state.context_window) + .chars() + .count() as u16; + paint_status_marker(area, area.y, buf, label, counter_cols); + } let autocomplete_visible = self.app_state.autocomplete.visible; let palette_cap = if area.height >= 20 { From 2fb6a4574e29800898485a217cd47ffb9e320e84 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 13 Sep 2026 18:27:01 +0000 Subject: [PATCH 2/3] test(core): make fade-in/out start assertions scheduler-safe nextest can delay a worker tens of milliseconds between Fade construction and progress(), so a 100ms fade-in was already past mid-curve on CI. Use a long duration and reset() immediately before sampling. Co-authored-by: Mathis --- src/cortex-core/src/animation/mod.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/cortex-core/src/animation/mod.rs b/src/cortex-core/src/animation/mod.rs index b47bb867..f4da12d2 100644 --- a/src/cortex-core/src/animation/mod.rs +++ b/src/cortex-core/src/animation/mod.rs @@ -121,23 +121,31 @@ mod tests { #[test] fn test_fade_in() { - let fade = Fade::fade_in(100); + // Long duration so a loaded nextest worker cannot advance past mid-fade + // between construction and the first progress() sample. + let mut fade = Fade::fade_in(60_000); + fade.reset(); assert!(!fade.is_complete()); assert_eq!(fade.direction(), FadeDirection::In); - // Initial progress should be near 0 let initial = fade.progress(); - assert!(initial < 0.5); + assert!( + initial < 0.5, + "fresh fade-in should start near 0, got {initial}" + ); } #[test] fn test_fade_out() { - let fade = Fade::fade_out(100); + let mut fade = Fade::fade_out(60_000); + fade.reset(); assert_eq!(fade.direction(), FadeDirection::Out); - // Initial progress should be near 1 (since it's fading out) let initial = fade.progress(); - assert!(initial > 0.5); + assert!( + initial > 0.5, + "fresh fade-out should start near 1, got {initial}" + ); } #[test] From de4cba2a704bb2d3800e4776682a64c7c982c857 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 13 Sep 2026 18:40:29 +0000 Subject: [PATCH 3/3] test(core): sample fade constructor without reset Keep the 60s duration for nextest scheduling, but assert constructor progress() directly so fade_in/fade_out start timestamps stay covered. Co-authored-by: Mathis --- src/cortex-core/src/animation/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cortex-core/src/animation/mod.rs b/src/cortex-core/src/animation/mod.rs index f4da12d2..0689bffd 100644 --- a/src/cortex-core/src/animation/mod.rs +++ b/src/cortex-core/src/animation/mod.rs @@ -123,8 +123,7 @@ mod tests { fn test_fade_in() { // Long duration so a loaded nextest worker cannot advance past mid-fade // between construction and the first progress() sample. - let mut fade = Fade::fade_in(60_000); - fade.reset(); + let fade = Fade::fade_in(60_000); assert!(!fade.is_complete()); assert_eq!(fade.direction(), FadeDirection::In); @@ -137,8 +136,7 @@ mod tests { #[test] fn test_fade_out() { - let mut fade = Fade::fade_out(60_000); - fade.reset(); + let fade = Fade::fade_out(60_000); assert_eq!(fade.direction(), FadeDirection::Out); let initial = fade.progress();