diff options
| author | Adam Malczewski <[email protected]> | 2026-06-10 11:40:16 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-10 11:40:16 +0900 |
| commit | 7b345f132763fa6405ae858b74e46229629c19d9 (patch) | |
| tree | 4600200e5a92eccbe880f46b3760cf0b1217737d /src/features/tabs/tabs.test.ts | |
| parent | 89ca80bac1e143a4ec5ba6e2e1d4998acce2553c (diff) | |
| download | dispatch-web-7b345f132763fa6405ae858b74e46229629c19d9.tar.gz dispatch-web-7b345f132763fa6405ae858b74e46229629c19d9.zip | |
feat(tabs,app): tab id handles, fixed-width tabs-lift, slim shell + full-height sidebar
Tabs:
- short-handle ID badge per tab (shortest unique conversationId prefix, min 4)
- fixed-width (w-48) tabs with tabs-lift folder borders
Shell (composition root):
- drop the Dispatch title bar; tabs sit at the very top with a 5px gap
- big faded "Dispatch" watermark centered on an empty chat
- collapsible right sidebar (empty shell) spanning full window height: a
permanently right-pinned hamburger in the tab row toggles it; in-flow push
that shrinks the whole left column (tabs included) at >=lg, overlay + backdrop
below lg; open-by-default on wide / closed on narrow
- main is overflow-hidden with a min-w-0 shrink chain; app.css pins
html/body/#app height + body overflow hidden so the page never overflows
Diffstat (limited to 'src/features/tabs/tabs.test.ts')
| -rw-r--r-- | src/features/tabs/tabs.test.ts | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/src/features/tabs/tabs.test.ts b/src/features/tabs/tabs.test.ts index 5effa9b..3c2a8c2 100644 --- a/src/features/tabs/tabs.test.ts +++ b/src/features/tabs/tabs.test.ts @@ -7,10 +7,12 @@ import { deriveTitle, initialState, isStuckToEnd, + MIN_HANDLE_LENGTH, newDraft, selectTab, setModel, setTitle, + shortHandle, } from "./tabs"; const tab = (conversationId: string, model = "default", title = "Chat"): Tab => ({ @@ -213,3 +215,34 @@ describe("isStuckToEnd", () => { expect(isStuckToEnd({ scrollLeft: 499, clientWidth: 500, scrollWidth: 1000 })).toBe(false); }); }); + +describe("shortHandle", () => { + it("uses the minimum length when the id is unique", () => { + const h = shortHandle("3f9a1b2c-aaaa", ["3f9a1b2c-aaaa", "7c2d-bbbb"]); + expect(h).toBe("3f9a"); + expect(h.length).toBe(MIN_HANDLE_LENGTH); + }); + + it("grows the prefix until unique among open tabs", () => { + // two ids share the first 5 chars → handle grows to 6 to disambiguate + expect(shortHandle("abcde1-xxxx", ["abcde1-xxxx", "abcde2-yyyy"])).toBe("abcde1"); + expect(shortHandle("abcde2-yyyy", ["abcde1-xxxx", "abcde2-yyyy"])).toBe("abcde2"); + }); + + it("shrinks back to the minimum when the colliding sibling is gone", () => { + expect(shortHandle("abcde1-xxxx", ["abcde1-xxxx"])).toBe("abcd"); + }); + + it("ignores the id itself when present in the list", () => { + expect(shortHandle("deadbeef", ["deadbeef"])).toBe("dead"); + }); + + it("returns the whole id when shorter than the minimum length", () => { + expect(shortHandle("ab", ["ab", "cd"])).toBe("ab"); + }); + + it("falls back to the full id when one id is a prefix of another", () => { + // "abcd" is a prefix of "abcd1234" → no unique shorter prefix exists for it + expect(shortHandle("abcd", ["abcd", "abcd1234"])).toBe("abcd"); + }); +}); |
