summaryrefslogtreecommitdiffhomepage
path: root/src/features/tabs/tabs.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-07 14:49:30 +0900
committerAdam Malczewski <[email protected]>2026-06-07 14:49:30 +0900
commit29aef69f00906a7ef973bd68df7a56c7a212206f (patch)
tree4229199b4aed1d9225a50ca95e05ffb8f204e418 /src/features/tabs/tabs.ts
parent0cb08678ffead285afb1f93ba50cd5a144ed5e7d (diff)
downloaddispatch-web-main.tar.gz
dispatch-web-main.zip
feat(tabs): polish new-chat button — stuck-only square edge, New Chat labelHEADmain
- isStuckToEnd (pure): square the sticky '+' right edge only while it floats over scrolled tabs; rounded at rest. Edge-measured in TabBar via a disposed scroll + ResizeObserver effect (RO guarded for non-browser envs). - Show a temporary 'New Chat' title when the draft is selected, with the '+' moved to the trailing close-button slot for consistency with real tabs.
Diffstat (limited to 'src/features/tabs/tabs.ts')
-rw-r--r--src/features/tabs/tabs.ts20
1 files changed, 20 insertions, 0 deletions
diff --git a/src/features/tabs/tabs.ts b/src/features/tabs/tabs.ts
index 9af522f..a4db6f3 100644
--- a/src/features/tabs/tabs.ts
+++ b/src/features/tabs/tabs.ts
@@ -66,6 +66,26 @@ export function activeTab(state: TabsState): Tab | null {
return state.tabs.find((t) => t.conversationId === state.activeConversationId) ?? null;
}
+export interface ScrollMetrics {
+ readonly scrollLeft: number;
+ readonly clientWidth: number;
+ readonly scrollWidth: number;
+}
+
+const STUCK_EPSILON = 1;
+
+/**
+ * True when a right-pinned sticky element is floating over scrolled content — the
+ * strip overflows horizontally AND is not scrolled fully to the right. When it is
+ * at rest (no overflow, or scrolled to the end so it sits at its natural position)
+ * this returns false. Pure: layout measurements in, boolean out.
+ */
+export function isStuckToEnd(m: ScrollMetrics): boolean {
+ const overflows = m.scrollWidth > m.clientWidth + STUCK_EPSILON;
+ const notAtEnd = m.scrollLeft + m.clientWidth < m.scrollWidth - STUCK_EPSILON;
+ return overflows && notAtEnd;
+}
+
export function deriveTitle(message: string, max: number = DEFAULT_MAX_TITLE_LENGTH): string {
const trimmed = message.trim().replace(/\s+/g, " ");
if (trimmed.length === 0) return DEFAULT_TITLE;