chromium-tabs 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/core/list-selection-model.ts","../src/core/types.ts","../src/core/tab-strip-model.ts","../src/core/tab-lifecycle-manager.ts","../src/react/use-tab-strip.ts","../src/react/use-tab-drag.ts","../src/react/tab-panels.tsx","../src/react/tab-strip.tsx","../src/react/group-header.tsx","../src/react/tab-item.tsx","../src/react/tabs.tsx"],"sourcesContent":["export * from './core/index'\nexport { useTabStrip, useTabStripModel, type TabStripSnapshot } from './react/use-tab-strip'\nexport { useTabDrag } from './react/use-tab-drag'\nexport { Tabs, type TabsProps } from './react/tabs'\nexport { TabStrip, type TabStripProps } from './react/tab-strip'\nexport {\n TabPanels,\n useTabVisibility,\n type TabPanelsProps,\n type TabVisibility,\n} from './react/tab-panels'\nexport { TabItem, type TabItemProps } from './react/tab-item'\nexport { GroupHeader, GROUP_COLOR_VALUES, type GroupHeaderProps } from './react/group-header'\n","/**\n * Port of ui/base/models/list_selection_model.{h,cc}.\n *\n * Selection model represented as a list of indexes. In addition to the set of\n * selected indices it maintains:\n *\n * - active: the index of the currently visible item, or null if nothing is\n * selected.\n * - anchor: the index of the last item the user clicked on. Extending the\n * selection extends it from this index. Null if nothing is selected.\n *\n * Typically there is one selected item, in which case anchor and active are\n * the same.\n */\n\n// list_selection_model.cc:21\nfunction indexAfterInsertion(insertPosition: number, originalIndex: number): number {\n return originalIndex >= insertPosition ? originalIndex + 1 : originalIndex\n}\n\n// list_selection_model.cc:40 — null means \"erase from container\"\nfunction indexAfterRemoval(removePosition: number, originalIndex: number): number | null {\n if (originalIndex === removePosition) return null\n return originalIndex > removePosition ? originalIndex - 1 : originalIndex\n}\n\n// list_selection_model.cc:68 — assumes destination <= source (move to lower index)\nfunction indexAfterMove(\n sourcePosition: number,\n destinationPosition: number,\n rangeSize: number,\n originalIndex: number | null,\n): number | null {\n if (originalIndex === null) return null\n if (destinationPosition <= originalIndex && originalIndex < sourcePosition + rangeSize) {\n if (originalIndex < sourcePosition) {\n // Items in [destination, source) see rangeSize items inserted before\n // them, so their indices increase.\n return originalIndex + rangeSize\n }\n // Items in [source, source + rangeSize) shift down by\n // (source - destination) spots.\n return originalIndex - (sourcePosition - destinationPosition)\n }\n return originalIndex\n}\n\nexport class ListSelectionModel {\n private selectedIndices_ = new Set<number>()\n private active_: number | null = null\n private anchor_: number | null = null\n\n get anchor(): number | null {\n return this.anchor_\n }\n\n setAnchor(anchor: number | null): void {\n this.anchor_ = anchor\n }\n\n get active(): number | null {\n return this.active_\n }\n\n setActive(active: number | null): void {\n this.active_ = active\n }\n\n /** True if nothing is selected. */\n get empty(): boolean {\n return this.selectedIndices_.size === 0\n }\n\n get size(): number {\n return this.selectedIndices_.size\n }\n\n /** Selected indices in ascending order. */\n selectedIndices(): number[] {\n return [...this.selectedIndices_].sort((a, b) => a - b)\n }\n\n /**\n * Increments all indices >= index. Used when a new item is inserted.\n * list_selection_model.cc:112\n */\n incrementFrom(index: number): void {\n const next = new Set<number>()\n for (const i of this.selectedIndices_) next.add(indexAfterInsertion(index, i))\n this.selectedIndices_ = next\n this.anchor_ = this.anchor_ === null ? null : indexAfterInsertion(index, this.anchor_)\n this.active_ = this.active_ === null ? null : indexAfterInsertion(index, this.active_)\n }\n\n /**\n * Shifts all indices > index down by 1; index itself is removed from the\n * selection. Used when an item is removed. list_selection_model.cc:122\n */\n decrementFrom(index: number): void {\n const next = new Set<number>()\n for (const i of this.selectedIndices_) {\n const v = indexAfterRemoval(index, i)\n if (v !== null) next.add(v)\n }\n this.selectedIndices_ = next\n this.anchor_ = this.anchor_ === null ? null : indexAfterRemoval(index, this.anchor_)\n this.active_ = this.active_ === null ? null : indexAfterRemoval(index, this.active_)\n }\n\n /** Sets the anchor, active and selection to index. */\n setSelectedIndex(index: number | null): void {\n this.anchor_ = index\n this.active_ = index\n this.selectedIndices_.clear()\n if (index !== null) this.selectedIndices_.add(index)\n }\n\n isSelected(index: number): boolean {\n return this.selectedIndices_.has(index)\n }\n\n /** Adds index to the selection without changing active or anchor. */\n addIndexToSelection(index: number): void {\n this.selectedIndices_.add(index)\n }\n\n /** Adds [indexStart, indexEnd] inclusive without changing active or anchor. */\n addIndexRangeToSelection(indexStart: number, indexEnd: number): void {\n if (indexStart > indexEnd) throw new RangeError('indexStart must be <= indexEnd')\n for (let i = indexStart; i <= indexEnd; i++) this.selectedIndices_.add(i)\n }\n\n /** Removes index from the selection without changing active or anchor. */\n removeIndexFromSelection(index: number): void {\n this.selectedIndices_.delete(index)\n }\n\n /**\n * Sets the selection to the range anchor..index. If there is no anchor,\n * behaves like setSelectedIndex. list_selection_model.cc:171\n */\n setSelectionFromAnchorTo(index: number): void {\n if (this.anchor_ === null) {\n this.setSelectedIndex(index)\n return\n }\n this.selectedIndices_.clear()\n const min = Math.min(index, this.anchor_)\n const max = Math.max(index, this.anchor_)\n for (let i = min; i <= max; i++) this.selectedIndices_.add(i)\n this.active_ = index\n }\n\n /**\n * Makes sure anchor..index are selected, adding to the existing selection.\n * list_selection_model.cc:186\n */\n addSelectionFromAnchorTo(index: number): void {\n if (this.anchor_ === null) {\n this.setSelectedIndex(index)\n return\n }\n const min = Math.min(index, this.anchor_)\n const max = Math.max(index, this.anchor_)\n for (let i = min; i <= max; i++) this.selectedIndices_.add(i)\n this.active_ = index\n }\n\n /**\n * Invoked when `length` items move from oldIndex to newIndex. If moving to\n * a greater index, newIndex is the index *after* removing the moved range.\n * list_selection_model.cc:199\n */\n move(oldIndex: number, newIndex: number, length: number): void {\n if (oldIndex === newIndex) throw new RangeError('oldIndex must differ from newIndex')\n if (length <= 0) throw new RangeError('length must be > 0')\n\n // Remap move-to-higher-index to the equivalent move-to-lower-index\n // operation (\"ABCDEFG\" -> \"CDEFABG\" is 'AB' up by 4, or 'CDEF' down by 2).\n if (newIndex > oldIndex) {\n this.move(oldIndex + length, oldIndex, newIndex - oldIndex)\n return\n }\n\n this.anchor_ = indexAfterMove(oldIndex, newIndex, length, this.anchor_)\n this.active_ = indexAfterMove(oldIndex, newIndex, length, this.active_)\n\n const next = new Set<number>()\n for (const i of this.selectedIndices_) {\n const v = indexAfterMove(oldIndex, newIndex, length, i)\n if (v !== null) next.add(v)\n }\n this.selectedIndices_ = next\n }\n\n /** Clears the selection, anchor and active. */\n clear(): void {\n this.anchor_ = null\n this.active_ = null\n this.selectedIndices_.clear()\n }\n\n clone(): ListSelectionModel {\n const copy = new ListSelectionModel()\n copy.selectedIndices_ = new Set(this.selectedIndices_)\n copy.active_ = this.active_\n copy.anchor_ = this.anchor_\n return copy\n }\n\n equals(other: ListSelectionModel): boolean {\n if (this.active_ !== other.active_ || this.anchor_ !== other.anchor_) return false\n if (this.selectedIndices_.size !== other.selectedIndices_.size) return false\n for (const i of this.selectedIndices_) {\n if (!other.selectedIndices_.has(i)) return false\n }\n return true\n }\n\n /** 'active=X anchor=X selection=X X X...' — matches the C++ ToString. */\n toString(): string {\n const opt = (v: number | null) => (v === null ? '<none>' : String(v))\n return `active=${opt(this.active_)} anchor=${opt(this.anchor_)} selection=${this.selectedIndices().join(' ')}`\n }\n}\n","/**\n * Core types. Ported from chromium-reference/chrome/browser/ui/tabs/tab_enums.h\n * and the supporting types in tab_strip_model.h.\n *\n * Chrome's tabs carry a WebContents. This library carries a generic `data: T`\n * payload instead so it works for any application.\n */\n\n/** Sentinel for \"no tab\" — mirrors TabStripModel::kNoTab. */\nexport const NO_TAB = -1\n\nexport type TabId = string\nexport type TabGroupId = string\n\n/**\n * How a tab is being opened. Trimmed from ui::PageTransition to the two\n * qualities the TabStripModel actually branches on (tab_strip_model.cc:1731,\n * 1786): link-like openings (inherit opener, insert adjacent) and typed/manual\n * openings (append at end, transient opener at end-of-strip).\n */\nexport type TabOpenCause = 'link' | 'typed' | 'other'\n\n/** Bitmask flags used when adding tabs. Mirrors AddTabTypes (tab_enums.h:42). */\nexport const AddTabFlags = {\n NONE: 0,\n /** The tab should become the active tab. */\n ACTIVE: 1 << 0,\n /** The tab should be pinned. */\n PINNED: 1 << 1,\n /**\n * Use the caller-supplied index rather than letting the model determine\n * the position from the open cause and opener relationships.\n */\n FORCE_INDEX: 1 << 2,\n /** Set the new tab's opener to the currently active tab. */\n INHERIT_OPENER: 1 << 3,\n} as const\n\n/** Bitmask flags used when closing tabs. Mirrors TabCloseTypes (tab_enums.h:26). */\nexport const CloseTabFlags = {\n NONE: 0,\n /** The close was triggered directly by a user gesture. */\n USER_GESTURE: 1 << 0,\n} as const\n\n/**\n * Visual data attached to a tab group. Mirrors tab_groups::TabGroupVisualData.\n * Chrome cycles through 9 named colors; we keep the names so UIs can map them\n * to whatever palette they like.\n */\nexport type TabGroupColor =\n | 'grey'\n | 'blue'\n | 'red'\n | 'yellow'\n | 'green'\n | 'pink'\n | 'purple'\n | 'cyan'\n | 'orange'\n\nexport const TAB_GROUP_COLORS: readonly TabGroupColor[] = [\n 'grey',\n 'blue',\n 'red',\n 'yellow',\n 'green',\n 'pink',\n 'purple',\n 'cyan',\n 'orange',\n]\n\nexport interface TabGroupVisualData {\n title: string\n color: TabGroupColor\n isCollapsed: boolean\n}\n\n/**\n * A tab in the strip. Identity is the object (like Chrome's TabModel pointer);\n * `id` exists for React keys and serialization.\n */\nexport interface Tab<T = unknown> {\n readonly id: TabId\n /** Application payload (your \"WebContents\"). */\n data: T\n /** The tab that opened this tab, if tracked. Mirrors TabModel::opener(). */\n opener: Tab<T> | null\n /**\n * When true, the opener relationship is cleared the next time the active\n * tab changes. Set for typed new-tabs at end of strip (\"quick look-up\"\n * pattern, tab_strip_model.cc:1804-1810).\n */\n resetOpenerOnActiveTabChange: boolean\n /** Pinned tabs are locked to the left side of the strip. */\n pinned: boolean\n /** Group membership, if any. Pinned tabs are never grouped. */\n group: TabGroupId | null\n /** Blocked by a modal — selectable but flagged. Mirrors TabModel::blocked. */\n blocked: boolean\n /**\n * True when the tab's content has been dropped to save memory. The tab\n * stays in the strip (title intact via `data`); content remounts fresh\n * when the tab is next activated. Mirrors TabLifecycleUnit::is_discarded_\n * (tab_lifecycle_unit.cc:312) and WebContents::WasDiscarded.\n */\n discarded: boolean\n /**\n * Wall-clock ms of the last time this tab stopped being active, or\n * Infinity while it is active. Used for least-recently-used discard\n * ordering. Mirrors last_focused_time_ (tab_lifecycle_unit.cc:142, which\n * uses Time::Max() while focused).\n */\n lastActiveAt: number\n /**\n * Per-tab opt-out from automatic discarding. Mirrors\n * TabLifecycleUnit::auto_discardable_ (the extensions setAutoDiscardable\n * API surface).\n */\n autoDiscardable: boolean\n}\n\nexport interface TabGroup {\n readonly id: TabGroupId\n visualData: TabGroupVisualData\n}\n\n/** Options for TabStripModel.addTab / insertTabAt. */\nexport interface AddTabOptions {\n /** Target index. Omit (or pass NO_TAB) to let the model decide. */\n index?: number\n /** What kind of action opened this tab. Default 'other'. */\n cause?: TabOpenCause\n /** Bitmask of AddTabFlags. */\n flags?: number\n /** Insert directly into an existing group. */\n group?: TabGroupId\n /** Stable id; generated if omitted. */\n id?: TabId\n}\n\n/** Desired state of one tab, for TabStripModel.reconcile. */\nexport interface ReconcileTab<T> {\n id: TabId\n data: T\n /** Defaults to false. The list should be pinned-first-consistent. */\n pinned?: boolean\n}\n\nexport interface ReconcileOptions<T> {\n /** Tab to end up active. Omit to leave activation to the model. */\n activeId?: TabId | null\n /** Equality for data payloads; defaults to Object.is. */\n dataEquals?: (a: T, b: T) => boolean\n}\n\nexport interface TabStripModelOptions<T> {\n /**\n * Veto tab closes (Chrome: IsTabClosable / policy). Return false to keep\n * the tab open; observers get a tabCloseCancelled event.\n */\n canCloseTab?: (tab: Tab<T>) => boolean\n /** Disable the group feature entirely (Chrome: null TabGroupModelFactory). */\n supportsGroups?: boolean\n /** Custom id generator for tabs and groups. */\n generateId?: () => string\n}\n","/**\n * Port of chrome/browser/ui/tabs/tab_strip_model.{h,cc} (Chromium main\n * @ 3dbd2135). Line references in comments point into chromium-reference/.\n *\n * Differences from Chrome are listed in PORTING_NOTES.md. The big ones:\n * tabs carry generic `data: T` instead of WebContents, split tabs are not\n * ported, and there is no async unload-handler dance — closes are\n * synchronous, vetoable via `canCloseTab`.\n *\n * Like Chrome, the model maintains these invariants:\n * - all pinned tabs occur before all non-pinned tabs\n * - tabs of a group are contiguous, and never pinned\n * - the active tab is always valid while the strip is non-empty\n */\n\nimport { ListSelectionModel } from './list-selection-model'\nimport type {\n TabStripModelChange,\n TabStripModelObserver,\n TabStripSelectionChange,\n} from './observer'\nimport {\n AddTabFlags,\n NO_TAB,\n TAB_GROUP_COLORS,\n type AddTabOptions,\n type ReconcileOptions,\n type ReconcileTab,\n type Tab,\n type TabGroup,\n type TabGroupId,\n type TabGroupVisualData,\n type TabId,\n type TabOpenCause,\n type TabStripModelOptions,\n} from './types'\n\nfunction defaultGenerateId(): string {\n return `t${Math.random().toString(36).slice(2, 10)}`\n}\n\n/** [start, end) index range, mirroring gfx::Range usage for group spans. */\nexport interface IndexRange {\n start: number\n end: number\n}\n\ninterface ActivateOptions {\n /**\n * Marks the activation as a direct user gesture. Mirrors\n * TabStripUserGestureDetails; gates the opener-forgetting heuristic\n * (tab_strip_model.cc:5301).\n */\n userGesture?: boolean\n}\n\n/** Internal: 'keep' preserves each tab's current group (group block moves). */\ntype GroupAssignment = TabGroupId | null | 'keep'\n\nexport class TabStripModel<T = unknown> {\n private tabs_: Array<Tab<T>> = []\n private groups_ = new Map<TabGroupId, TabGroupVisualData>()\n\n // Selection is tracked by tab identity, mirroring Chrome's\n // TabStripModelSelectionState; index views are derived on demand.\n private selectedTabs_ = new Set<Tab<T>>()\n private activeTab_: Tab<T> | null = null\n private anchorTab_: Tab<T> | null = null\n\n private observers_ = new Set<TabStripModelObserver<T>>()\n private closingAll_ = false\n private reentrancyGuard_ = false\n\n private readonly canCloseTab_: (tab: Tab<T>) => boolean\n private readonly supportsGroups_: boolean\n private readonly generateId_: () => string\n\n constructor(options: TabStripModelOptions<T> = {}) {\n this.canCloseTab_ = options.canCloseTab ?? (() => true)\n this.supportsGroups_ = options.supportsGroups ?? true\n this.generateId_ = options.generateId ?? defaultGenerateId\n }\n\n // Basic queries ////////////////////////////////////////////////////////////\n\n get count(): number {\n return this.tabs_.length\n }\n\n get empty(): boolean {\n return this.tabs_.length === 0\n }\n\n /** True while closeAllTabs is in progress. */\n get closingAll(): boolean {\n return this.closingAll_\n }\n\n containsIndex(index: number): boolean {\n return index >= 0 && index < this.tabs_.length\n }\n\n getTabAt(index: number): Tab<T> {\n const tab = this.tabs_[index]\n if (!tab) throw new RangeError(`no tab at index ${index}`)\n return tab\n }\n\n indexOfTab(tab: Tab<T> | null): number {\n if (!tab) return NO_TAB\n return this.tabs_.indexOf(tab)\n }\n\n getTabById(id: TabId): Tab<T> | null {\n return this.tabs_.find((t) => t.id === id) ?? null\n }\n\n /** Snapshot of the tabs in strip order. */\n getTabs(): ReadonlyArray<Tab<T>> {\n return [...this.tabs_]\n }\n\n get activeTab(): Tab<T> | null {\n return this.activeTab_\n }\n\n get activeIndex(): number {\n return this.activeTab_ ? this.indexOfTab(this.activeTab_) : NO_TAB\n }\n\n /** Index of the first non-pinned tab; count if all pinned. (cc:1418 area) */\n indexOfFirstNonPinnedTab(): number {\n const i = this.tabs_.findIndex((t) => !t.pinned)\n return i === -1 ? this.tabs_.length : i\n }\n\n isTabPinned(index: number): boolean {\n return this.getTabAt(index).pinned\n }\n\n isTabBlocked(index: number): boolean {\n return this.getTabAt(index).blocked\n }\n\n isTabSelected(index: number): boolean {\n return this.selectedTabs_.has(this.getTabAt(index))\n }\n\n /** Derived index-based view of the selection (Chrome: GetListSelectionModel). */\n selectionModel(): ListSelectionModel {\n const model = new ListSelectionModel()\n for (const tab of this.selectedTabs_) {\n const i = this.indexOfTab(tab)\n if (i !== NO_TAB) model.addIndexToSelection(i)\n }\n model.setActive(this.activeTab_ ? this.indexOfTab(this.activeTab_) : null)\n model.setAnchor(this.anchorTab_ ? this.indexOfTab(this.anchorTab_) : null)\n return model\n }\n\n // Group queries //////////////////////////////////////////////////////////\n\n get supportsTabGroups(): boolean {\n return this.supportsGroups_\n }\n\n getTabGroupForTab(index: number): TabGroupId | null {\n if (!this.containsIndex(index)) return null\n return this.tabs_[index]!.group\n }\n\n getGroups(): TabGroup[] {\n return [...this.groups_.entries()].map(([id, visualData]) => ({ id, visualData }))\n }\n\n getGroupVisualData(group: TabGroupId): TabGroupVisualData | null {\n return this.groups_.get(group) ?? null\n }\n\n containsGroup(group: TabGroupId): boolean {\n return this.groups_.has(group)\n }\n\n /** [start, end) range of the group's tabs. Mirrors TabGroup::ListTabs. */\n listTabsInGroup(group: TabGroupId): IndexRange {\n let start = -1\n let end = -1\n for (let i = 0; i < this.tabs_.length; i++) {\n if (this.tabs_[i]!.group === group) {\n if (start === -1) start = i\n end = i + 1\n }\n }\n if (start === -1) throw new Error(`no such group: ${group}`)\n return { start, end }\n }\n\n isGroupCollapsed(group: TabGroupId): boolean {\n return this.groups_.get(group)?.isCollapsed ?? false\n }\n\n /** True if the tab is inside a collapsed group. (cc:1423) */\n isTabCollapsed(index: number): boolean {\n const group = this.getTabGroupForTab(index)\n return group !== null && this.isGroupCollapsed(group)\n }\n\n /**\n * If a tab inserted at index would land inside a group, returns that group.\n * Returns null at the first index of a group (a tab there sits between\n * groups, not inside one). Mirrors GetSurroundingTabGroup.\n */\n getSurroundingTabGroup(index: number): TabGroupId | null {\n const before = this.getTabGroupForTab(index - 1)\n const at = this.getTabGroupForTab(index)\n return before !== null && before === at ? before : null\n }\n\n // Observers //////////////////////////////////////////////////////////////\n\n addObserver(observer: TabStripModelObserver<T>): () => void {\n this.observers_.add(observer)\n return () => this.observers_.delete(observer)\n }\n\n removeObserver(observer: TabStripModelObserver<T>): void {\n this.observers_.delete(observer)\n }\n\n // Add / insert ///////////////////////////////////////////////////////////\n\n /**\n * Command-level add: picks the position from the open cause and opener\n * relationships, then inserts. Port of AddTab (cc:1715-1828).\n */\n addTab(data: T, options: AddTabOptions = {}): Tab<T> {\n this.checkReentrancy_()\n const cause: TabOpenCause = options.cause ?? 'other'\n const flags = options.flags ?? 0\n let index = options.index ?? NO_TAB\n let group = options.group ?? null\n\n let inheritOpener = (flags & AddTabFlags.INHERIT_OPENER) !== 0\n\n if (cause === 'link' && (flags & AddTabFlags.FORCE_INDEX) === 0) {\n // Tabs opened via links are part of the same task as their parent.\n index = this.determineInsertionIndex(cause, (flags & AddTabFlags.ACTIVE) !== 0)\n inheritOpener = true\n // The active tab is our opener; inherit its group too. (cc:1742)\n if (group === null) {\n group = this.getTabGroupForTab(this.activeIndex)\n }\n } else if (index < 0 || index > this.count) {\n index = this.count\n }\n\n if (this.supportsGroups_) {\n if (group !== null && this.groups_.has(group)) {\n // Clamp so the group stays contiguous. (cc:1759)\n const range = this.listTabsInGroup(group)\n index = Math.min(Math.max(index, range.start), range.end)\n } else if (\n group === null &&\n this.getTabGroupForTab(index - 1) !== null &&\n this.getTabGroupForTab(index - 1) === this.getTabGroupForTab(index)\n ) {\n // Inserting between two tabs of the same group adopts it. (cc:1767)\n group = this.getTabGroupForTab(index)\n }\n // Pinned tabs cannot be grouped. (cc:1771)\n if (flags & AddTabFlags.PINNED) group = null\n } else {\n group = null\n }\n\n // A tab opened at the end of the strip by a typed action inherits the\n // opener too — the \"quick look-up\" pattern. Closing it returns to the\n // previous tab. (cc:1786-1796)\n if (cause === 'typed' && index === this.count) {\n inheritOpener = true\n }\n\n const tab = this.createTab_(data, options.id)\n this.insertTabAtImpl_(\n index,\n tab,\n flags | (inheritOpener ? AddTabFlags.INHERIT_OPENER : 0),\n group,\n )\n\n if (inheritOpener && cause === 'typed') {\n tab.resetOpenerOnActiveTabChange = true\n }\n return tab\n }\n\n /** Adds a tab at the end of the strip. Port of AppendTab. */\n appendTab(data: T, foreground = true): Tab<T> {\n return this.addTab(data, {\n index: this.count,\n cause: 'other',\n flags: AddTabFlags.FORCE_INDEX | (foreground ? AddTabFlags.ACTIVE : 0),\n })\n }\n\n /**\n * Inserts at the given index, only adjusting it to keep pinned tabs at the\n * front. Does NOT consult the order controller. Port of InsertWebContentsAt.\n * Returns the index actually used.\n */\n insertTabAt(index: number, data: T, options: Omit<AddTabOptions, 'index' | 'cause'> = {}): Tab<T> {\n this.checkReentrancy_()\n const group = options.group ?? null\n if (group !== null && !this.groups_.has(group)) {\n throw new Error(`no such group: ${group}`)\n }\n const tab = this.createTab_(data, options.id)\n this.insertTabAtImpl_(index, tab, options.flags ?? 0, group)\n return tab\n }\n\n // Activate / selection ///////////////////////////////////////////////////\n\n /** Makes the tab at index active. Port of ActivateTabAt (cc:1022). */\n activateTabAt(index: number, options: ActivateOptions = {}): void {\n this.checkReentrancy_()\n if (!this.containsIndex(index)) throw new RangeError(`no tab at index ${index}`)\n const tab = this.tabs_[index]!\n this.setSelection_(\n () => this.setSelectedTab_(tab),\n options.userGesture ? 'userGesture' : 'none',\n )\n }\n\n /** Extends the selection from the anchor to index. Port of cc:1514. */\n extendSelectionTo(index: number): void {\n this.checkReentrancy_()\n const tab = this.getTabAt(index)\n this.setSelection_(() => {\n if (!this.anchorTab_) {\n this.setSelectedTab_(tab)\n return\n }\n const anchorIndex = this.indexOfTab(this.anchorTab_)\n const lo = Math.min(anchorIndex, index)\n const hi = Math.max(anchorIndex, index)\n this.selectedTabs_ = new Set(this.tabs_.slice(lo, hi + 1))\n this.activeTab_ = tab\n }, 'none')\n }\n\n /** Adds the tab at index to the selection and makes it active+anchor. (cc:1545) */\n selectTabAt(index: number): void {\n this.checkReentrancy_()\n const tab = this.getTabAt(index)\n this.setSelection_(() => {\n this.selectedTabs_.add(tab)\n this.anchorTab_ = tab\n this.activeTab_ = tab\n }, 'none')\n }\n\n /** Removes the tab at index from the selection. No-op if it's the last one. (cc:1570) */\n deselectTabAt(index: number): void {\n this.checkReentrancy_()\n const tab = this.getTabAt(index)\n if (!this.selectedTabs_.has(tab)) return\n if (this.selectedTabs_.size === 1) return\n this.setSelection_(() => {\n this.selectedTabs_.delete(tab)\n const first = this.firstSelectedTab_()\n if (!this.activeTab_ || this.activeTab_ === tab) this.activeTab_ = first\n if (!this.anchorTab_ || this.anchorTab_ === tab) this.anchorTab_ = first\n }, 'none')\n }\n\n /** Selects anchor..index, adding to the current selection. (cc:1616) */\n addSelectionFromAnchorTo(index: number): void {\n this.checkReentrancy_()\n const tab = this.getTabAt(index)\n this.setSelection_(() => {\n if (!this.anchorTab_) {\n this.setSelectedTab_(tab)\n return\n }\n const anchorIndex = this.indexOfTab(this.anchorTab_)\n const lo = Math.min(anchorIndex, index)\n const hi = Math.max(anchorIndex, index)\n for (const t of this.tabs_.slice(lo, hi + 1)) this.selectedTabs_.add(t)\n this.activeTab_ = tab\n }, 'none')\n }\n\n /** Replaces the selection with the given index-based model. */\n setSelectionFromModel(source: ListSelectionModel): void {\n this.checkReentrancy_()\n if (source.active === null) throw new Error('selection must have an active index')\n this.setSelection_(() => {\n this.selectedTabs_ = new Set(source.selectedIndices().map((i) => this.getTabAt(i)))\n this.activeTab_ = this.getTabAt(source.active!)\n this.anchorTab_ = source.anchor === null ? null : this.getTabAt(source.anchor)\n }, 'none')\n }\n\n /**\n * Activates the next/previous tab, wrapping around and skipping collapsed\n * groups. Port of SelectRelativeTab (cc:3951).\n */\n selectNextTab(options: ActivateOptions = {}): void {\n this.selectRelativeTab_(1, options)\n }\n\n selectPreviousTab(options: ActivateOptions = {}): void {\n this.selectRelativeTab_(-1, options)\n }\n\n selectLastTab(options: ActivateOptions = {}): void {\n if (this.empty) return\n this.activateTabAt(this.count - 1, options)\n }\n\n private selectRelativeTab_(delta: 1 | -1, options: ActivateOptions): void {\n if (this.empty) return\n const startIndex = this.activeIndex\n let index = (startIndex + this.count + delta) % this.count\n let group = this.getTabGroupForTab(index)\n while (group !== null && this.isGroupCollapsed(group)) {\n index = (index + this.count + delta) % this.count\n group = this.getTabGroupForTab(index)\n }\n this.activateTabAt(index, options)\n }\n\n // Move ///////////////////////////////////////////////////////////////////\n\n /**\n * Moves the tab at index to toPosition (clamped so pinned tabs stay\n * together). Group membership adjusts to keep groups contiguous. Port of\n * MoveWebContentsAt (cc:1053). Returns the final index.\n */\n moveTabTo(index: number, toPosition: number, selectAfterMove = false): number {\n this.checkReentrancy_()\n if (!this.containsIndex(index)) throw new RangeError(`no tab at index ${index}`)\n const pinned = this.isTabPinned(index)\n toPosition = this.constrainMoveIndex_(toPosition, pinned)\n if (index === toPosition) return toPosition\n const group = this.getGroupToAssign_(index, toPosition)\n this.moveTabToIndexImpl_(index, toPosition, group, pinned, selectAfterMove)\n return toPosition\n }\n\n /**\n * Moves the selected tabs to index, pinned tabs first as a chunk, then\n * unpinned. `index` is interpreted as if the strip did not contain the\n * selected tabs. Port of MoveSelectedTabsTo (cc:1089).\n */\n moveSelectedTabsTo(index: number, group: TabGroupId | null = null): void {\n this.checkReentrancy_()\n const pinnedTabCount = this.indexOfFirstNonPinnedTab()\n const pinnedSelected = this.selectedIndices_().filter((i) => i < pinnedTabCount)\n const unpinnedSelected = this.selectedIndices_().filter((i) => i >= pinnedTabCount)\n\n const lastPinnedIndex = clamp(\n index + pinnedSelected.length - 1,\n pinnedSelected.length - 1,\n pinnedTabCount - 1,\n )\n this.moveTabsToIndexImpl_(pinnedSelected, lastPinnedIndex - pinnedSelected.length + 1, 'keep')\n\n const firstUnpinnedIndex = clamp(\n index + pinnedSelected.length,\n pinnedTabCount,\n this.count - unpinnedSelected.length,\n )\n this.moveTabsToIndexImpl_(unpinnedSelected, firstUnpinnedIndex, group)\n }\n\n /** Moves all tabs of a group to toIndex. Port of MoveGroupTo (cc:1117). */\n moveGroupTo(group: TabGroupId, toIndex: number): void {\n this.checkReentrancy_()\n if (!this.groups_.has(group)) throw new Error(`no such group: ${group}`)\n toIndex = this.constrainMoveIndex_(toIndex, false)\n const range = this.listTabsInGroup(group)\n if (range.start === toIndex) return\n const indices = []\n for (let i = range.start; i < range.end; i++) indices.push(i)\n // Block destination is in post-removal coordinates.\n const length = indices.length\n const dest = clamp(\n toIndex > range.start ? toIndex - length + 1 : toIndex,\n this.indexOfFirstNonPinnedTab() - countLessThan(indices, this.indexOfFirstNonPinnedTab()),\n this.count - length,\n )\n this.moveTabsToIndexImpl_(indices, dest, 'keep')\n this.notifyAll_((o) => o.onTabGroupChanged?.({ type: 'moved', groupId: group }))\n }\n\n /**\n * Moves the active tab one slot right/left. At a group boundary the tab\n * first changes group membership without moving; collapsed neighbor groups\n * are hopped over entirely. Port of MoveTabRelative (cc:3976).\n */\n moveTabNext(): void {\n this.moveTabRelative_(1)\n }\n\n moveTabPrevious(): void {\n this.moveTabRelative_(-1)\n }\n\n private moveTabRelative_(delta: 1 | -1): void {\n this.checkReentrancy_()\n const start = this.activeIndex\n if (start === NO_TAB) throw new Error('no active tab')\n\n let targetIndex = start\n const neighborIndex = delta === 1 ? start + 1 : start - 1\n if (this.containsIndex(neighborIndex) && this.isTabPinned(start) === this.isTabPinned(neighborIndex)) {\n targetIndex += delta\n }\n\n const currentGroup = this.getTabGroupForTab(start)\n let targetGroup = targetIndex === start ? null : this.getTabGroupForTab(neighborIndex)\n\n if (this.supportsGroups_ && currentGroup !== targetGroup) {\n if (currentGroup !== null) {\n // Leave the current group before moving out of its span.\n targetIndex = start\n targetGroup = null\n } else if (targetGroup !== null) {\n if (this.isGroupCollapsed(targetGroup)) {\n // Hop over the collapsed group as if it were one tab.\n const range = this.listTabsInGroup(targetGroup)\n targetIndex = delta === 1 ? range.end - 1 : range.start\n targetGroup = null\n } else {\n // Enter the group without moving.\n targetIndex = start\n }\n }\n }\n this.moveTabsToIndexImpl_([start], targetIndex, targetGroup)\n }\n\n // Pinning ////////////////////////////////////////////////////////////////\n\n /**\n * Pins or unpins the tab, moving it to the pinned/unpinned boundary.\n * Pinning removes the tab from its group. Returns the final index.\n * Port of SetTabPinned (cc:1407) + SetTabPinnedImpl (cc:5052).\n */\n setTabPinned(index: number, pinned: boolean): number {\n this.checkReentrancy_()\n if (!this.containsIndex(index)) throw new RangeError(`no tab at index ${index}`)\n if (this.isTabPinned(index) === pinned) return index\n const finalIndex = pinned\n ? this.indexOfFirstNonPinnedTab()\n : this.indexOfFirstNonPinnedTab() - 1\n this.moveTabToIndexImpl_(index, finalIndex, null, pinned, false)\n return finalIndex\n }\n\n // Close //////////////////////////////////////////////////////////////////\n\n /** Closes the tab at index. Returns true if it closed (not vetoed). */\n closeTabAt(index: number): boolean {\n return this.closeTabs_([this.getTabAt(index)])\n }\n\n /** Closes the tabs at the given indices. */\n closeTabsAt(indices: number[]): boolean {\n return this.closeTabs_(indices.map((i) => this.getTabAt(i)))\n }\n\n /** Port of CloseSelectedTabs. */\n closeSelectedTabs(): boolean {\n return this.closeTabs_([...this.selectedTabs_])\n }\n\n /** Port of CloseAllTabs (cc:455). */\n closeAllTabs(): boolean {\n return this.closeTabs_([...this.tabs_])\n }\n\n /** Context-menu style helper: close every tab except the one at index. */\n closeOtherTabs(index: number): boolean {\n const keep = this.getTabAt(index)\n return this.closeTabs_(this.tabs_.filter((t) => t !== keep && !t.pinned))\n }\n\n /** Context-menu style helper: close unpinned tabs to the right of index. */\n closeTabsToRight(index: number): boolean {\n return this.closeTabs_(this.tabs_.slice(index + 1).filter((t) => !t.pinned))\n }\n\n /** Closes all tabs in a group. Port of CloseAllTabsInGroup. */\n closeAllTabsInGroup(group: TabGroupId): boolean {\n const range = this.listTabsInGroup(group)\n return this.closeTabs_(this.tabs_.slice(range.start, range.end))\n }\n\n private closeTabs_(tabs: Array<Tab<T>>, options: { bypassVeto?: boolean } = {}): boolean {\n this.checkReentrancy_()\n const closable: Array<Tab<T>> = []\n for (const tab of tabs) {\n if (options.bypassVeto || this.canCloseTab_(tab)) {\n closable.push(tab)\n } else {\n this.notifyAll_((o) => o.onTabCloseCancelled?.(tab))\n }\n }\n if (closable.length === 0) return false\n\n const closingAll = closable.length === this.count\n if (closingAll) {\n this.closingAll_ = true\n this.notifyAll_((o) => o.willCloseAllTabs?.())\n }\n\n const oldActive = this.activeTab_\n const oldModel = this.selectionModel()\n const removed: Array<{ tab: Tab<T>; index: number }> = []\n const groupNotifications: Array<{ tab: Tab<T>; index: number; group: TabGroupId }> = []\n\n for (const tab of closable) {\n const index = this.indexOfTab(tab)\n if (index === NO_TAB) continue\n this.removeTabFromIndexImpl_(index)\n removed.push({ tab, index })\n if (tab.group !== null) {\n groupNotifications.push({ tab, index, group: tab.group })\n tab.group = null\n }\n }\n\n this.validate_()\n\n const selection = this.buildSelectionChange_(oldActive, oldModel, 'none')\n this.notifyAll_((o) =>\n o.onTabStripModelChanged?.({ type: 'removed', contents: removed }, selection),\n )\n for (const { tab, index, group } of groupNotifications) {\n this.notifyAll_((o) => o.onTabGroupedStateChanged?.(group, null, tab, index))\n }\n // Delete groups that became empty.\n for (const { group } of groupNotifications) {\n if (this.groups_.has(group) && !this.tabs_.some((t) => t.group === group)) {\n this.groups_.delete(group)\n this.notifyAll_((o) => o.onTabGroupChanged?.({ type: 'closed', groupId: group }))\n }\n }\n this.handleActiveTabChanged_(selection)\n\n if (closingAll) {\n this.closingAll_ = false\n this.notifyAll_((o) => o.closeAllTabsStopped?.('completed'))\n }\n return true\n }\n\n /**\n * Removes the tab and fixes up the selection. Port of\n * RemoveTabFromIndexImpl (cc:4551). Caller batches notifications.\n */\n private removeTabFromIndexImpl_(index: number): Tab<T> {\n const tab = this.tabs_[index]!\n const nextSelectedIndex = this.determineNewSelectedIndex_(index)\n\n this.fixOpeners_(index)\n this.tabs_.splice(index, 1)\n this.selectedTabs_.delete(tab)\n if (this.anchorTab_ === tab) this.anchorTab_ = null\n\n if (this.empty) {\n this.selectedTabs_.clear()\n this.activeTab_ = null\n this.anchorTab_ = null\n } else if (this.activeTab_ === tab) {\n if (this.selectedTabs_.size > 0) {\n // Active tab removed but something is still selected: first selected\n // tab becomes active and anchor. (cc:4595)\n const first = this.firstSelectedTab_()\n this.activeTab_ = first\n this.anchorTab_ = first\n } else {\n // Nothing selected: fall back to the order-controller choice. (cc:4601)\n if (nextSelectedIndex === null) throw new Error('invariant: no next tab to select')\n this.setSelectedTab_(this.tabs_[nextSelectedIndex]!)\n }\n }\n return tab\n }\n\n // External-state reconciliation //////////////////////////////////////////\n\n /**\n * Converges the strip to an external source-of-truth list with minimal\n * mutations (no remove-all/re-add), so an app whose canonical tab state\n * lives elsewhere (a router, a store, another window) can mirror it into\n * the model without losing tab identity, content state, or discard status.\n *\n * - tabs absent from `desired` are removed, bypassing `canCloseTab` (the\n * external state has already decided)\n * - missing tabs are inserted at their position under the given id\n * - `data` is swapped via setTabData where `dataEquals` reports a change\n * - pinned state and order converge to the desired list; pass a\n * pinned-first-consistent list or Chrome's clamping rules win\n * - `activeId`, when provided and present, is activated last\n *\n * Observers fire for each underlying mutation as usual; reconcile-driven\n * activations carry reason 'none', so a consumer that also writes model\n * changes back to the external store can tell them from user gestures.\n * Groups are not reconciled. No Chrome equivalent: this is integration\n * surface for embedding apps.\n */\n reconcile(desired: ReadonlyArray<ReconcileTab<T>>, options: ReconcileOptions<T> = {}): void {\n this.checkReentrancy_()\n const dataEquals = options.dataEquals ?? Object.is\n const desiredIds = new Set<TabId>()\n for (const want of desired) {\n if (desiredIds.has(want.id)) throw new Error(`duplicate tab id in reconcile: ${want.id}`)\n desiredIds.add(want.id)\n }\n\n const toRemove = this.tabs_.filter((tab) => !desiredIds.has(tab.id))\n if (toRemove.length > 0) this.closeTabs_(toRemove, { bypassVeto: true })\n\n desired.forEach((want, position) => {\n const existing = this.getTabById(want.id)\n const pinned = want.pinned ?? false\n if (!existing) {\n this.insertTabAt(position, want.data, {\n id: want.id,\n flags: pinned ? AddTabFlags.PINNED : AddTabFlags.NONE,\n })\n return\n }\n if (!dataEquals(existing.data, want.data)) {\n this.setTabData(this.indexOfTab(existing), want.data)\n }\n if (existing.pinned !== pinned) {\n this.setTabPinned(this.indexOfTab(existing), pinned)\n }\n const index = this.indexOfTab(existing)\n if (index !== position) {\n this.moveTabTo(index, position)\n }\n })\n\n if (options.activeId != null) {\n const activeIndex = this.indexOfTab(this.getTabById(options.activeId))\n if (activeIndex !== NO_TAB && activeIndex !== this.activeIndex) {\n this.activateTabAt(activeIndex)\n }\n }\n }\n\n // Openers ////////////////////////////////////////////////////////////////\n\n getOpenerOfTabAt(index: number): Tab<T> | null {\n return this.getTabAt(index).opener\n }\n\n setOpenerOfTabAt(index: number, opener: Tab<T> | null): void {\n const tab = this.getTabAt(index)\n if (opener === tab) throw new Error('a tab cannot be its own opener')\n if (opener && this.indexOfTab(opener) === NO_TAB) {\n throw new Error('opener must be in this tab strip')\n }\n tab.opener = opener\n }\n\n /** Port of ForgetAllOpeners (cc:3376). */\n forgetAllOpeners(): void {\n for (const tab of this.tabs_) tab.opener = null\n }\n\n forgetOpener(tab: Tab<T>): void {\n tab.opener = null\n }\n\n /**\n * Call when the user navigates a tab. Typed-style navigations reset all\n * opener relationships (the user started a new task), except in a fresh\n * end-of-strip tab. Port of TabNavigating (cc:1378).\n */\n tabNavigating(tab: Tab<T>, cause: TabOpenCause): void {\n if (cause !== 'typed') return\n const isNewTabAtEnd =\n tab === this.tabs_[this.tabs_.length - 1] && tab.resetOpenerOnActiveTabChange\n if (!isNewTabAtEnd) this.forgetAllOpeners()\n }\n\n /**\n * Index of the last tab opened (transitively) by the tab at startIndex,\n * scanning right, skipping pinned tabs, stopping at the first unrelated\n * unpinned tab. Port of GetIndexOfLastWebContentsOpenedBy (cc:1351).\n */\n getIndexOfLastTabOpenedBy(opener: Tab<T>, startIndex: number): number {\n const openerAndDescendants = new Set<Tab<T>>([opener])\n let lastIndex = NO_TAB\n for (let i = startIndex + 1; i < this.count; i++) {\n const tab = this.tabs_[i]!\n if (!tab.opener || !openerAndDescendants.has(tab.opener)) {\n if (tab.pinned) continue\n break\n }\n openerAndDescendants.add(tab)\n lastIndex = i\n }\n return lastIndex\n }\n\n // Groups /////////////////////////////////////////////////////////////////\n\n /**\n * Creates a group containing the tabs at indices (ascending). Tabs are\n * unpinned and made contiguous without splitting other groups. Returns the\n * group id. Port of AddToNewGroup (cc:671) + AddToNewGroupImpl (cc:4344).\n */\n addToNewGroup(indices: number[], visualData?: Partial<TabGroupVisualData>): TabGroupId {\n this.checkReentrancy_()\n this.requireGroups_()\n assertAscending(indices)\n if (indices.length === 0) throw new Error('indices must not be empty')\n\n const groupId = this.generateId_()\n this.groups_.set(groupId, {\n title: visualData?.title ?? '',\n color: visualData?.color ?? this.nextGroupColor_(),\n isCollapsed: visualData?.isCollapsed ?? false,\n })\n this.notifyAll_((o) => o.onTabGroupChanged?.({ type: 'created', groupId }))\n\n // Find a destination for the first tab that's not pinned or inside\n // another group; the rest stack up to its right. (cc:4376)\n const firstGroup = this.getTabGroupForTab(indices[0]!)\n let destinationIndex = -1\n for (let i = indices[0]!; i <= this.count; i++) {\n if (!this.containsIndex(i)) {\n destinationIndex = i\n break\n }\n if (this.isTabPinned(i)) continue\n const destinationGroup = this.getTabGroupForTab(i)\n if (destinationGroup === null || destinationGroup !== firstGroup) {\n destinationIndex = i\n break\n }\n }\n\n this.moveTabsAndSetProperties_(indices, destinationIndex, groupId, false)\n\n // Deselect all grouped tabs except the active one. (cc:4404)\n const range = this.listTabsInGroup(groupId)\n for (let i = range.start; i < range.end; i++) {\n if (this.activeIndex !== i && this.isTabSelected(i)) this.deselectTabAt(i)\n }\n return groupId\n }\n\n /**\n * Adds the tabs at indices (ascending) to an existing group. Tabs left of\n * the group move to its start, tabs right of it to its end; addToEnd sends\n * everything to the end. Port of AddToExistingGroup (cc:4415).\n */\n addToExistingGroup(indices: number[], group: TabGroupId, addToEnd = false): void {\n this.checkReentrancy_()\n this.requireGroups_()\n assertAscending(indices)\n if (!this.groups_.has(group)) return\n\n const range = this.listTabsInGroup(group)\n const firstTabIndex = range.start\n const lastTabIndex = range.end - 1\n\n const tabsLeftOfGroup = indices.filter((i) => i < firstTabIndex)\n const tabsRightOfGroup = indices.filter((i) => i > lastTabIndex)\n\n if (addToEnd) {\n this.moveTabsAndSetProperties_(\n [...tabsLeftOfGroup, ...tabsRightOfGroup],\n lastTabIndex + 1,\n group,\n false,\n )\n } else {\n this.moveTabsAndSetProperties_(tabsLeftOfGroup, firstTabIndex, group, false)\n this.moveTabsAndSetProperties_(tabsRightOfGroup, lastTabIndex + 1, group, false)\n }\n }\n\n /**\n * Removes the tabs at indices (ascending) from their groups. Tabs in the\n * first half of a group exit left of it, the rest exit right. Port of\n * RemoveFromGroup (cc:4253 area) + SeparateTabsByVisualPosition.\n */\n removeFromGroup(indices: number[]): void {\n this.checkReentrancy_()\n this.requireGroups_()\n assertAscending(indices)\n\n const indicesPerGroup = new Map<TabGroupId, number[]>()\n for (const index of indices) {\n const group = this.getTabGroupForTab(index)\n if (group !== null) {\n if (!indicesPerGroup.has(group)) indicesPerGroup.set(group, [])\n indicesPerGroup.get(group)!.push(index)\n }\n }\n\n for (const [group, groupIndices] of indicesPerGroup) {\n const range = this.listTabsInGroup(group)\n const firstTabIndex = range.start\n const lastTabIndex = range.end - 1\n const midpoint = Math.floor((range.end - range.start) / 2)\n\n const leftOfGroup = groupIndices.filter((i) => i - firstTabIndex < midpoint)\n const rightOfGroup = groupIndices.filter((i) => i - firstTabIndex >= midpoint)\n\n this.moveTabsAndSetProperties_(leftOfGroup, firstTabIndex, null, false)\n this.moveTabsAndSetProperties_(rightOfGroup, lastTabIndex + 1, null, false)\n }\n }\n\n /** Updates a group's title/color/collapsed state. Port of ChangeTabGroupVisuals. */\n updateGroupVisuals(group: TabGroupId, visuals: Partial<TabGroupVisualData>): void {\n const old = this.groups_.get(group)\n if (!old) throw new Error(`no such group: ${group}`)\n const next: TabGroupVisualData = { ...old, ...visuals }\n if (old.isCollapsed !== next.isCollapsed && next.isCollapsed) {\n // Collapsing the group containing the active tab moves activation to\n // the nearest expanded tab; if there is none the collapse is refused\n // (Chrome opens a new tab in that case — we have no tab factory).\n const range = this.listTabsInGroup(group)\n const active = this.activeIndex\n if (active >= range.start && active < range.end) {\n this.groups_.set(group, next)\n const fallback = this.getNextExpandedActiveTab_(range.start, range.end)\n if (fallback === null) {\n this.groups_.set(group, old)\n throw new Error('cannot collapse the only expanded tabs in the strip')\n }\n this.activateTabAt(fallback)\n this.notifyAll_((o) =>\n o.onTabGroupChanged?.({ type: 'visualsChanged', groupId: group, oldVisuals: old, newVisuals: next }),\n )\n return\n }\n }\n this.groups_.set(group, next)\n this.notifyAll_((o) =>\n o.onTabGroupChanged?.({ type: 'visualsChanged', groupId: group, oldVisuals: old, newVisuals: next }),\n )\n }\n\n setGroupCollapsed(group: TabGroupId, collapsed: boolean): void {\n this.updateGroupVisuals(group, { isCollapsed: collapsed })\n }\n\n /** Chrome's TabGroupModel::GetNextColor: least-used color, in palette order. */\n private nextGroupColor_(): TabGroupVisualData['color'] {\n const usage = new Map<string, number>()\n for (const color of TAB_GROUP_COLORS) usage.set(color, 0)\n for (const { color } of this.groups_.values()) {\n usage.set(color, (usage.get(color) ?? 0) + 1)\n }\n let best = TAB_GROUP_COLORS[0]!\n for (const color of TAB_GROUP_COLORS) {\n if (usage.get(color)! < usage.get(best)!) best = color\n }\n return best\n }\n\n // Tab data / state ///////////////////////////////////////////////////////\n\n /** Swaps the tab's data payload. Emits a 'replaced' change (Chrome: Replace). */\n setTabData(index: number, data: T): void {\n const tab = this.getTabAt(index)\n const oldData = tab.data\n tab.data = data\n const selection = this.buildSelectionChange_(this.activeTab_, this.selectionModel(), 'none')\n this.notifyAll_((o) =>\n o.onTabStripModelChanged?.(\n { type: 'replaced', tab, oldData, newData: data, index },\n selection,\n ),\n )\n }\n\n /** Notify observers the tab changed in place (after mutating tab.data). */\n notifyTabChanged(index: number): void {\n const tab = this.getTabAt(index)\n this.notifyAll_((o) => o.onTabChanged?.(tab, index))\n }\n\n /** Port of SetTabBlocked (cc:1397). */\n setTabBlocked(index: number, blocked: boolean): void {\n const tab = this.getTabAt(index)\n if (tab.blocked === blocked) return\n tab.blocked = blocked\n this.notifyAll_((o) => o.onTabChanged?.(tab, index))\n }\n\n // Lifecycle (discarding) /////////////////////////////////////////////////\n\n /**\n * Drops the tab's content to save memory while keeping the tab in the\n * strip. The active tab cannot be discarded (it's visible). Content\n * remounts fresh on the next activation, like Chrome's reload-on-focus.\n * Port of TabLifecycleUnit::Discard (tab_lifecycle_unit.cc:346) +\n * TabStripModel::DiscardWebContentsAt semantics. Returns true on success.\n */\n discardTabAt(index: number): boolean {\n const tab = this.getTabAt(index)\n if (tab.discarded) return false\n if (tab === this.activeTab_) return false\n tab.discarded = true\n this.notifyAll_((o) => o.onTabDiscardedStateChanged?.(tab, index, true))\n return true\n }\n\n /**\n * Restores a discarded tab without activating it (Chrome: reloading a\n * background discarded tab, DidStartLoading path).\n */\n restoreTabAt(index: number): boolean {\n const tab = this.getTabAt(index)\n if (!tab.discarded) return false\n this.restoreTab_(tab)\n return true\n }\n\n /** Per-tab opt-out from automatic discarding (extensions setAutoDiscardable). */\n setTabAutoDiscardable(index: number, autoDiscardable: boolean): void {\n this.getTabAt(index).autoDiscardable = autoDiscardable\n }\n\n isTabDiscarded(index: number): boolean {\n return this.getTabAt(index).discarded\n }\n\n /** Number of tabs whose content is currently live (not discarded). */\n get loadedTabCount(): number {\n return this.tabs_.reduce((n, t) => n + (t.discarded ? 0 : 1), 0)\n }\n\n private restoreTab_(tab: Tab<T>): void {\n tab.discarded = false\n const index = this.indexOfTab(tab)\n this.notifyAll_((o) => o.onTabDiscardedStateChanged?.(tab, index, false))\n }\n\n // Order controller ///////////////////////////////////////////////////////\n\n /**\n * Where to place a newly opened tab. Port of DetermineInsertionIndex\n * (cc:5329).\n */\n determineInsertionIndex(cause: TabOpenCause, foreground: boolean): number {\n if (this.count === 0) return 0\n\n if (cause === 'link' && this.activeIndex !== NO_TAB) {\n if (foreground) {\n // Opened in the foreground from a link: insert adjacent to the opener.\n return this.activeIndex + 1\n }\n const opener = this.activeTab_!\n const index = this.getIndexOfLastTabOpenedBy(opener, this.activeIndex)\n if (index === NO_TAB) return this.activeIndex + 1\n\n // Insert before the first group discontinuity after the opener.\n // (cc:5351, crbug.com/40789226)\n const openerGroup = this.getTabGroupForTab(this.activeIndex)\n for (let i = this.activeIndex + 1; i <= index; i++) {\n if (this.getTabGroupForTab(i) !== openerGroup) return i\n }\n return index + 1\n }\n // Ctrl+T and friends: open at the end of the strip.\n return this.count\n }\n\n /**\n * Which tab should become active after the tab at `index` closes.\n * Port of DetermineNewSelectedIndex (cc:5377), single-tab block, with the\n * \"parent collection\" preference specialized to groups. Returns the index\n * in post-close coordinates, or null if this is the last tab.\n */\n private determineNewSelectedIndex_(index: number): number | null {\n if (this.count === 1) return null\n const blockStart = index\n const blockEnd = index + 1\n\n const afterClosing = (i: number) => (i > blockEnd - 1 ? i - 1 : i)\n\n // First preference: a tab this tab opened. (cc:5407)\n let next = this.getIndexOfNextTabOpenedBy_(blockStart, blockEnd)\n if (next !== NO_TAB && !this.isTabCollapsed(next)) return afterClosing(next)\n\n // Second preference: a tab opened by this tab's opener. (cc:5414)\n next = this.getIndexOfNextTabOpenedByOpenerOf_(blockStart, blockEnd)\n if (next !== NO_TAB && !this.isTabCollapsed(next)) return afterClosing(next)\n\n // Third preference: the opener itself. (cc:5422)\n const opener = this.tabs_[index]!.opener\n if (opener) {\n const openerIndex = this.indexOfTab(opener)\n if (openerIndex !== NO_TAB && openerIndex !== index && !this.isTabCollapsed(openerIndex)) {\n return afterClosing(openerIndex)\n }\n }\n\n // Fourth preference: stay inside the closing tab's group. (cc:5432)\n const group = this.tabs_[index]!.group\n if (group !== null) {\n const range = this.listTabsInGroup(group)\n if (range.end !== blockEnd) return afterClosing(blockEnd)\n if (range.start !== blockStart) return afterClosing(blockStart - 1)\n }\n\n // Otherwise pick the nearest non-collapsed tab. (cc:5467)\n const nextAvailable = this.getNextExpandedActiveTab_(blockStart, blockEnd)\n if (nextAvailable !== null) return afterClosing(nextAvailable)\n\n // Fall back to the neighbor. (cc:5473)\n if (blockEnd - 1 >= this.count - 1) return blockStart - 1\n return blockEnd - 1\n }\n\n /** Port of GetIndexOfNextWebContentsOpenedBy (cc:3286). */\n private getIndexOfNextTabOpenedBy_(blockStart: number, blockEnd: number): number {\n const blockTabs = new Set(this.tabs_.slice(blockStart, blockEnd))\n for (let i = blockEnd; i < this.count; i++) {\n const opener = this.tabs_[i]!.opener\n if (opener && blockTabs.has(opener)) return i\n }\n for (let i = blockStart - 1; i >= 0; i--) {\n const opener = this.tabs_[i]!.opener\n if (opener && blockTabs.has(opener)) return i\n }\n return NO_TAB\n }\n\n /** Port of GetIndexOfNextWebContentsOpenedByOpenerOf (cc:3312). */\n private getIndexOfNextTabOpenedByOpenerOf_(blockStart: number, blockEnd: number): number {\n const blockOpeners = new Set<Tab<T>>()\n for (let i = blockStart; i < blockEnd; i++) {\n const opener = this.tabs_[i]!.opener\n if (opener) blockOpeners.add(opener)\n }\n if (blockOpeners.size === 0) return NO_TAB\n for (let i = blockEnd; i < this.count; i++) {\n const opener = this.tabs_[i]!.opener\n if (opener && blockOpeners.has(opener)) return i\n }\n for (let i = blockStart - 1; i >= 0; i--) {\n const opener = this.tabs_[i]!.opener\n if (opener && blockOpeners.has(opener)) return i\n }\n return NO_TAB\n }\n\n /** Port of GetNextExpandedActiveTab (cc:3346): right of block, then left. */\n private getNextExpandedActiveTab_(blockStart: number, blockEnd: number): number | null {\n for (let i = blockEnd; i < this.count; i++) {\n if (!this.isTabCollapsed(i)) return i\n }\n for (let i = blockStart - 1; i >= 0; i--) {\n if (!this.isTabCollapsed(i)) return i\n }\n return null\n }\n\n // Internal mechanics /////////////////////////////////////////////////////\n\n private createTab_(data: T, id?: TabId): Tab<T> {\n return {\n id: id ?? this.generateId_(),\n data,\n opener: null,\n resetOpenerOnActiveTabChange: false,\n pinned: false,\n group: null,\n blocked: false,\n discarded: false,\n lastActiveAt: Date.now(),\n autoDiscardable: true,\n }\n }\n\n /** Port of ConstrainInsertionIndex (cc:3408). */\n private constrainInsertionIndex_(index: number, pinned: boolean): number {\n return pinned\n ? clamp(index, 0, this.indexOfFirstNonPinnedTab())\n : clamp(index, this.indexOfFirstNonPinnedTab(), this.count)\n }\n\n /** Port of ConstrainMoveIndex (cc:3413). */\n private constrainMoveIndex_(index: number, pinned: boolean): number {\n return pinned\n ? clamp(index, 0, this.indexOfFirstNonPinnedTab() - 1)\n : clamp(index, this.indexOfFirstNonPinnedTab(), this.count - 1)\n }\n\n /** Port of InsertTabAtImpl (cc:3575). Returns the index actually used. */\n private insertTabAtImpl_(\n index: number,\n tab: Tab<T>,\n flags: number,\n group: TabGroupId | null,\n ): number {\n const active = (flags & AddTabFlags.ACTIVE) !== 0 || this.empty\n const pin = (flags & AddTabFlags.PINNED) !== 0\n index = this.constrainInsertionIndex_(index, pin)\n\n const activeTab = this.activeTab_\n if ((flags & AddTabFlags.INHERIT_OPENER) !== 0 && activeTab) {\n // Forget existing relationships first so multiple openers aren't live\n // at once. (cc:3602)\n if (active) this.forgetAllOpeners()\n tab.opener = activeTab\n }\n\n // InsertTabAtIndexImpl (cc:4504)\n tab.pinned = pin\n tab.group = pin ? null : group\n const oldActive = this.activeTab_\n this.tabs_.splice(index, 0, tab)\n const oldModel = this.selectionModel()\n if (active) this.setSelectedTab_(tab)\n this.validate_()\n\n const selection = this.buildSelectionChange_(oldActive, oldModel, 'none')\n this.notifyAll_((o) =>\n o.onTabStripModelChanged?.(\n { type: 'inserted', contents: [{ tab, index }] },\n selection,\n ),\n )\n if (tab.group !== null) {\n this.notifyAll_((o) => o.onTabGroupedStateChanged?.(null, tab.group, tab, index))\n }\n this.handleActiveTabChanged_(selection)\n return index\n }\n\n /**\n * Single-tab move with explicit final group/pin state. Port of\n * MoveTabToIndexImpl (cc:4617).\n */\n private moveTabToIndexImpl_(\n initialIndex: number,\n finalIndex: number,\n group: TabGroupId | null,\n pin: boolean,\n selectAfterMove: boolean,\n ): void {\n const tab = this.tabs_[initialIndex]!\n const initialPinned = tab.pinned\n const initialGroup = tab.group\n\n if (initialIndex === finalIndex && group === initialGroup && pin === initialPinned) return\n\n if (initialIndex !== finalIndex) this.fixOpeners_(initialIndex)\n\n const oldActive = this.activeTab_\n const oldModel = this.selectionModel()\n\n this.tabs_.splice(initialIndex, 1)\n this.tabs_.splice(finalIndex, 0, tab)\n tab.pinned = pin\n tab.group = pin ? null : group\n\n if (selectAfterMove) this.setSelectedTab_(tab)\n this.validate_()\n\n const selection = this.buildSelectionChange_(oldActive, oldModel, 'none')\n if (initialIndex !== finalIndex) {\n this.notifyAll_((o) =>\n o.onTabStripModelChanged?.(\n { type: 'moved', tab, fromIndex: initialIndex, toIndex: finalIndex },\n selection,\n ),\n )\n }\n if (initialPinned !== tab.pinned) {\n this.notifyAll_((o) => o.onTabPinnedStateChanged?.(tab, finalIndex))\n }\n this.emitGroupStateChange_(tab, finalIndex, initialGroup, tab.group)\n this.handleActiveTabChanged_(selection)\n }\n\n /**\n * Block move: removes the tabs at `indices`, reinserts them contiguously at\n * `destination` (post-removal coordinates), assigning the given group and\n * the pinned state of the first moving tab. Port of MoveTabsToIndexImpl\n * (cc:4698) + MoveTabsWithNotifications (cc:5081).\n */\n private moveTabsToIndexImpl_(\n indices: number[],\n destination: number,\n group: GroupAssignment,\n ): void {\n if (indices.length === 0) return\n assertAscending(indices)\n\n const pin = this.tabs_[indices[0]!]!.pinned\n const moving = indices.map((i) => this.tabs_[i]!)\n const initial = moving.map((tab, k) => ({\n tab,\n index: indices[k]!,\n group: tab.group,\n pinned: tab.pinned,\n }))\n\n const oldActive = this.activeTab_\n const oldModel = this.selectionModel()\n\n // FixOpeners for every tab that will change position. (PrepareTabsToMoveToIndex)\n for (const i of indices) this.fixOpeners_(i)\n\n const movingSet = new Set(moving)\n const remaining = this.tabs_.filter((t) => !movingSet.has(t))\n remaining.splice(destination, 0, ...moving)\n this.tabs_ = remaining\n for (const tab of moving) {\n tab.pinned = pin\n if (group !== 'keep') tab.group = pin ? null : group\n }\n this.validate_()\n\n const selection = this.buildSelectionChange_(oldActive, oldModel, 'none')\n for (const note of initial) {\n const finalIndex = this.indexOfTab(note.tab)\n if (note.index !== finalIndex) {\n this.notifyAll_((o) =>\n o.onTabStripModelChanged?.(\n { type: 'moved', tab: note.tab, fromIndex: note.index, toIndex: finalIndex },\n selection,\n ),\n )\n }\n if (note.pinned !== note.tab.pinned) {\n this.notifyAll_((o) => o.onTabPinnedStateChanged?.(note.tab, finalIndex))\n }\n this.emitGroupStateChange_(note.tab, finalIndex, note.group, note.tab.group)\n }\n this.handleActiveTabChanged_(selection)\n }\n\n /**\n * Port of MoveTabsAndSetPropertiesImpl (cc:4469): destination is given in\n * pre-removal coordinates and adjusted here.\n */\n private moveTabsAndSetProperties_(\n indices: number[],\n destinationIndex: number,\n group: TabGroupId | null,\n pinned: boolean,\n ): void {\n if (indices.length === 0) return\n let numTabsLeftOfDestination = 0\n for (const i of indices) {\n if (i >= destinationIndex) break\n numTabsLeftOfDestination++\n }\n void pinned // all current callers pass false; pin comes from the first moving tab\n this.moveTabsToIndexImpl_(indices, destinationIndex - numTabsLeftOfDestination, group)\n }\n\n /**\n * Group to assign when a tab moves from index to toPosition so groups stay\n * contiguous. Port of GetGroupToAssign (cc:5195).\n */\n private getGroupToAssign_(index: number, toPosition: number): TabGroupId | null {\n const tab = this.tabs_[index]!\n if (!this.supportsGroups_) return null\n\n let newLeftGroup: TabGroupId | null = null\n let newRightGroup: TabGroupId | null = null\n if (toPosition > index) {\n newLeftGroup = this.getTabGroupForTab(toPosition)\n newRightGroup = this.getTabGroupForTab(toPosition + 1)\n } else if (toPosition < index) {\n newLeftGroup = this.getTabGroupForTab(toPosition - 1)\n newRightGroup = this.getTabGroupForTab(toPosition)\n }\n\n if (tab.group !== newLeftGroup && tab.group !== newRightGroup) {\n if (newLeftGroup === newRightGroup && newLeftGroup !== null) {\n // Landing in the middle of an existing group: join it.\n return newLeftGroup\n }\n if (tab.group !== null && this.tabs_.filter((t) => t.group === tab.group).length > 1) {\n // Landing between groups while leaving a non-empty group behind:\n // clear membership so the old group stays contiguous.\n return null\n }\n }\n return tab.group\n }\n\n /**\n * Re-points the openers of any tab that referenced the tab at index at that\n * tab's own opener. Port of FixOpeners (cc:5171).\n */\n private fixOpeners_(index: number): void {\n const oldTab = this.tabs_[index]!\n const newOpener = oldTab.opener\n for (const tab of this.tabs_) {\n if (tab.opener !== oldTab) continue\n tab.opener = newOpener === tab ? null : newOpener\n }\n }\n\n /** Selection helpers (Chrome: TabStripModelSelectionState). */\n private setSelectedTab_(tab: Tab<T>): void {\n this.selectedTabs_ = new Set([tab])\n this.activeTab_ = tab\n this.anchorTab_ = tab\n }\n\n private firstSelectedTab_(): Tab<T> | null {\n let best: Tab<T> | null = null\n let bestIndex = Infinity\n for (const tab of this.selectedTabs_) {\n const i = this.indexOfTab(tab)\n if (i !== NO_TAB && i < bestIndex) {\n bestIndex = i\n best = tab\n }\n }\n return best\n }\n\n private selectedIndices_(): number[] {\n return [...this.selectedTabs_]\n .map((t) => this.indexOfTab(t))\n .filter((i) => i !== NO_TAB)\n .sort((a, b) => a - b)\n }\n\n /**\n * Wraps a selection mutation in change tracking + notification. Mirrors\n * SetSelection (cc:1178).\n */\n private setSelection_(mutate: () => void, reason: 'none' | 'userGesture'): void {\n const oldActive = this.activeTab_\n const oldModel = this.selectionModel()\n mutate()\n const selection = this.buildSelectionChange_(oldActive, oldModel, reason)\n if (selection.activeTabChanged || selection.selectionChanged) {\n this.notifyAll_((o) =>\n o.onTabStripModelChanged?.({ type: 'selectionOnly' }, selection),\n )\n this.handleActiveTabChanged_(selection)\n }\n }\n\n private buildSelectionChange_(\n oldTab: Tab<T> | null,\n oldModel: ListSelectionModel,\n reason: 'none' | 'userGesture',\n ): TabStripSelectionChange<T> {\n const newTab = this.activeTab_\n const newModel = this.selectionModel()\n return {\n oldTab,\n newTab,\n oldModel,\n newModel,\n reason,\n get activeTabChanged() {\n return oldTab !== newTab\n },\n get selectionChanged() {\n return !oldModel.equals(newModel)\n },\n }\n }\n\n /**\n * Opener and lifecycle bookkeeping when the active tab changes. Port of\n * OnActiveTabChanged (cc:5255) plus TabLifecycleUnit::SetFocused\n * (tab_lifecycle_unit.cc:135).\n */\n private handleActiveTabChanged_(selection: TabStripSelectionChange<T>): void {\n if (!selection.activeTabChanged || this.empty) return\n const oldTab = selection.oldTab\n const newTab = selection.newTab\n let oldOpener: Tab<T> | null = null\n\n // SetFocused(false) on the old tab: record when it left the foreground.\n // SetFocused(true) on the new tab: Infinity while focused, and a\n // discarded tab reloads on focus (MaybeLoad, tab_lifecycle_unit.cc:155).\n if (oldTab && this.indexOfTab(oldTab) !== NO_TAB) {\n oldTab.lastActiveAt = Date.now()\n }\n if (newTab) {\n newTab.lastActiveAt = Infinity\n if (newTab.discarded) this.restoreTab_(newTab)\n }\n\n if (oldTab && this.indexOfTab(oldTab) !== NO_TAB) {\n oldOpener = oldTab.opener\n // Transient opener relationships reset on any active-tab change. (cc:5292)\n if (oldTab.resetOpenerOnActiveTabChange) {\n oldTab.opener = null\n oldTab.resetOpenerOnActiveTabChange = false\n }\n }\n const newOpener = newTab?.opener ?? null\n\n // On a user-gesture switch outside the opener tree, forget all openers\n // to keep future close-activation predictable. (cc:5301)\n if (\n selection.reason === 'userGesture' &&\n newOpener !== oldOpener &&\n newOpener !== oldTab &&\n oldOpener !== newTab\n ) {\n this.forgetAllOpeners()\n }\n }\n\n private emitGroupStateChange_(\n tab: Tab<T>,\n index: number,\n oldGroup: TabGroupId | null,\n newGroup: TabGroupId | null,\n ): void {\n if (oldGroup === newGroup) return\n this.notifyAll_((o) => o.onTabGroupedStateChanged?.(oldGroup, newGroup, tab, index))\n if (oldGroup !== null && this.groups_.has(oldGroup) && !this.tabs_.some((t) => t.group === oldGroup)) {\n this.groups_.delete(oldGroup)\n this.notifyAll_((o) => o.onTabGroupChanged?.({ type: 'closed', groupId: oldGroup }))\n }\n }\n\n private notifyAll_(fn: (observer: TabStripModelObserver<T>) => void): void {\n // Observers must not mutate the model while being notified (Chrome:\n // ReentrancyCheck).\n this.reentrancyGuard_ = true\n try {\n for (const observer of [...this.observers_]) fn(observer)\n } finally {\n this.reentrancyGuard_ = false\n }\n }\n\n private requireGroups_(): void {\n if (!this.supportsGroups_) throw new Error('tab groups are disabled for this model')\n }\n\n private checkReentrancy_(): void {\n if (this.reentrancyGuard_) {\n throw new Error('TabStripModel is not re-entrant; do not mutate it from an observer')\n }\n }\n\n /**\n * Invariant validation. Port of CompleteModelUpdateTransaction; Chrome\n * CHECKs, we throw.\n */\n private validate_(): void {\n // Pinned tabs strictly before unpinned tabs.\n const firstNonPinned = this.indexOfFirstNonPinnedTab()\n for (let i = 0; i < this.tabs_.length; i++) {\n const tab = this.tabs_[i]!\n if (tab.pinned !== i < firstNonPinned) {\n throw new Error(`invariant violated: pinned tab at index ${i} after unpinned tabs`)\n }\n if (tab.pinned && tab.group !== null) {\n throw new Error(`invariant violated: pinned tab at index ${i} is grouped`)\n }\n }\n // Group contiguity.\n const seenGroups = new Set<TabGroupId>()\n let currentGroup: TabGroupId | null = null\n for (const tab of this.tabs_) {\n if (tab.group !== currentGroup) {\n if (tab.group !== null && seenGroups.has(tab.group)) {\n throw new Error(`invariant violated: group ${tab.group} is not contiguous`)\n }\n if (tab.group !== null) seenGroups.add(tab.group)\n currentGroup = tab.group\n }\n }\n // Active tab valid while non-empty.\n if (!this.empty && (!this.activeTab_ || this.indexOfTab(this.activeTab_) === NO_TAB)) {\n throw new Error('invariant violated: no valid active tab')\n }\n }\n}\n\nfunction clamp(value: number, lo: number, hi: number): number {\n return Math.min(Math.max(value, lo), hi)\n}\n\nfunction countLessThan(sorted: number[], threshold: number): number {\n let n = 0\n for (const v of sorted) {\n if (v < threshold) n++\n else break\n }\n return n\n}\n\nfunction assertAscending(indices: number[]): void {\n for (let i = 1; i < indices.length; i++) {\n if (indices[i]! <= indices[i - 1]!) {\n throw new Error('indices must be sorted in ascending order')\n }\n }\n}\n","/**\n * Automatic tab discarding. Port of Chrome's tab lifecycle stack:\n *\n * - chrome/browser/resource_coordinator/tab_manager.cc (orchestration)\n * - chrome/browser/performance_manager/policies/discard_eligibility_policy.h\n * (CanDiscardResult tri-state, CannotDiscardReason, the importance sort in\n * PageNodeSortProxy::operator<, kNonVisiblePagesUrgentProtectionTime)\n * - chrome/browser/resource_coordinator/tab_lifecycle_unit.cc (discard\n * mechanics live on the model as discardTabAt/restore-on-focus)\n *\n * Chrome triggers discards on OS memory pressure, which the web platform\n * does not expose reliably. The deterministic equivalent is a budget on the\n * number of loaded (mounted) tabs: when `maxLoadedTabs` is exceeded, the\n * least important tabs are discarded, in exactly Chrome's importance order.\n */\n\nimport type { TabStripModel } from './tab-strip-model'\nimport type { Tab } from './types'\n\n/** Mirrors LifecycleUnitDiscardReason (minus Chrome-internal variants). */\nexport type DiscardReason = 'proactive' | 'urgent' | 'external'\n\n/** Trimmed CannotDiscardReason (cannot_discard_reason.h). */\nexport type CannotDiscardReason =\n | 'activeTab'\n | 'alreadyDiscarded'\n | 'optedOut'\n | 'appVeto'\n | 'pinnedTab'\n | 'recentlyActive'\n\n/** Mirrors CanDiscardResult (discard_eligibility_policy.h:56). */\nexport type CanDiscardResult = 'eligible' | 'protected' | 'disallowed'\n\nexport interface CanDiscardDecision {\n result: CanDiscardResult\n reasons: CannotDiscardReason[]\n}\n\nexport interface TabLifecycleOptions<T> {\n /**\n * Maximum number of tabs whose content stays loaded at once. Exceeding it\n * triggers automatic discarding of the least important tabs. Pass null to\n * disable automatic discarding (manual discardLeastImportant still works).\n * Chrome's equivalent trigger is OS memory pressure.\n */\n maxLoadedTabs?: number | null\n /**\n * Tabs active within this window are protected (not disallowed) from\n * discarding. Mirrors kNonVisiblePagesUrgentProtectionTime, 10 minutes on\n * desktop (discard_eligibility_policy.h:38).\n */\n recentlyActiveProtectionMs?: number\n /**\n * Protect pinned tabs (CannotDiscardReason::kPinnedTab). Protected tabs\n * are only discarded when eligible tabs alone can't satisfy the budget.\n */\n protectPinnedTabs?: boolean\n /**\n * App-level veto, the equivalent of the protections Chrome detects in the\n * renderer (playing audio, form input, user edits, capturing, PiP...).\n * Return false to disallow discarding a tab.\n */\n canDiscardTab?: (tab: Tab<T>) => boolean\n /**\n * Called just before a tab's content is dropped, the equivalent of\n * WebContents::AboutToBeDiscarded. Last chance to snapshot restorable\n * state (scroll position, draft text) into tab.data.\n */\n onBeforeDiscard?: (tab: Tab<T>) => void\n /**\n * Apps whose tab content shares global state per content type (a\n * singleton store per route/scene) can return a key here: at most one\n * non-discarded tab per distinct key stays loaded. Whenever two loaded\n * tabs share a key, every one except the active (else the most recently\n * active) is discarded immediately, and reload-on-focus re-derives each\n * tab's state from its own `data` when it is next activated — shared\n * state can then never bleed between two visible-at-once duplicates.\n * Return null to exempt a tab (e.g. content that isolates correctly).\n *\n * This is a correctness policy, not a memory policy: it overrides the\n * pinned/recently-active protections and the `canDiscardTab` veto (the\n * active tab still always keeps its content). No Chrome equivalent —\n * Chrome tabs never share renderer state.\n */\n exclusiveContentKey?: (tab: Tab<T>) => string | null\n}\n\nconst DEFAULT_MAX_LOADED_TABS = 10\nconst DEFAULT_RECENTLY_ACTIVE_PROTECTION_MS = 10 * 60 * 1000\n\nexport class TabLifecycleManager<T = unknown> {\n private readonly model_: TabStripModel<T>\n private readonly maxLoadedTabs_: number | null\n private readonly recentlyActiveProtectionMs_: number\n private readonly protectPinnedTabs_: boolean\n private readonly canDiscardTab_: ((tab: Tab<T>) => boolean) | null\n private readonly onBeforeDiscard_: ((tab: Tab<T>) => void) | null\n private readonly exclusiveContentKey_: ((tab: Tab<T>) => string | null) | null\n private detach_: (() => void) | null = null\n private enforcePending_ = false\n\n constructor(model: TabStripModel<T>, options: TabLifecycleOptions<T> = {}) {\n this.model_ = model\n this.maxLoadedTabs_ = options.maxLoadedTabs === undefined ? DEFAULT_MAX_LOADED_TABS : options.maxLoadedTabs\n this.recentlyActiveProtectionMs_ =\n options.recentlyActiveProtectionMs ?? DEFAULT_RECENTLY_ACTIVE_PROTECTION_MS\n this.protectPinnedTabs_ = options.protectPinnedTabs ?? true\n this.canDiscardTab_ = options.canDiscardTab ?? null\n this.onBeforeDiscard_ = options.onBeforeDiscard ?? null\n this.exclusiveContentKey_ = options.exclusiveContentKey ?? null\n }\n\n /**\n * Starts observing the model and enforcing the loaded-tab budget. Returns\n * a stop function. Discards run on a microtask after model changes, never\n * re-entrantly (Chrome posts discard tasks for the same reason).\n */\n start(): () => void {\n if (this.detach_) return () => this.stop()\n const schedule = () => this.scheduleEnforce_()\n this.detach_ = this.model_.addObserver({\n onTabStripModelChanged: (change) => {\n // 'replaced' matters for exclusive content keys: a data swap can\n // navigate a tab into a key another loaded tab already holds.\n if (change.type === 'inserted' || change.type === 'selectionOnly' || change.type === 'replaced') {\n schedule()\n }\n },\n onTabChanged: () => schedule(),\n onTabDiscardedStateChanged: (_tab, _index, discarded) => {\n if (!discarded) schedule()\n },\n })\n this.scheduleEnforce_()\n return () => this.stop()\n }\n\n stop(): void {\n this.detach_?.()\n this.detach_ = null\n }\n\n /**\n * Tri-state discard eligibility for one tab. Port of\n * DiscardEligibilityPolicy::CanDiscard.\n */\n canDiscard(tab: Tab<T>): CanDiscardDecision {\n const reasons: CannotDiscardReason[] = []\n let result: CanDiscardResult = 'eligible'\n const disallow = (r: CannotDiscardReason) => {\n reasons.push(r)\n result = 'disallowed'\n }\n const protect = (r: CannotDiscardReason) => {\n reasons.push(r)\n if (result === 'eligible') result = 'protected'\n }\n\n if (tab.discarded) disallow('alreadyDiscarded')\n if (tab === this.model_.activeTab) disallow('activeTab')\n if (!tab.autoDiscardable) disallow('optedOut')\n if (this.canDiscardTab_ && !this.canDiscardTab_(tab)) disallow('appVeto')\n\n if (this.protectPinnedTabs_ && tab.pinned) protect('pinnedTab')\n if (Date.now() - tab.lastActiveAt < this.recentlyActiveProtectionMs_) {\n protect('recentlyActive')\n }\n return { result, reasons }\n }\n\n /**\n * Discard candidates in Chrome's importance order, least important first:\n * eligible before protected, each group least-recently-active first,\n * disallowed never. Port of PageNodeSortProxy::operator<\n * (discard_eligibility_policy.h:95) with focused/visible folded into\n * 'activeTab' (a single-window strip has one visible tab).\n */\n getDiscardCandidates(includeProtected: boolean): Array<Tab<T>> {\n const eligible: Array<Tab<T>> = []\n const protectedTabs: Array<Tab<T>> = []\n for (const tab of this.model_.getTabs()) {\n const decision = this.canDiscard(tab)\n if (decision.result === 'eligible') eligible.push(tab)\n else if (decision.result === 'protected') protectedTabs.push(tab)\n }\n const byLeastRecentlyActive = (a: Tab<T>, b: Tab<T>) => a.lastActiveAt - b.lastActiveAt\n eligible.sort(byLeastRecentlyActive)\n protectedTabs.sort(byLeastRecentlyActive)\n return includeProtected ? [...eligible, ...protectedTabs] : eligible\n }\n\n /**\n * Discards the least important discardable tab. 'urgent' may take\n * protected tabs (Chrome: urgent discarding under memory pressure);\n * 'proactive' and 'external' only take eligible ones. Port of\n * PageDiscardingHelper::DiscardAPage. Returns the discarded tab or null.\n */\n discardLeastImportant(reason: DiscardReason = 'proactive'): Tab<T> | null {\n const candidates = this.getDiscardCandidates(reason === 'urgent')\n const tab = candidates[0]\n if (!tab) return null\n this.discardTab_(tab)\n return tab\n }\n\n /**\n * Discards tabs until the loaded count fits the budget. Eligible tabs go\n * first; protected tabs are taken only if the budget still isn't met\n * (matching the sort order Chrome walks when reclaiming memory).\n * Disallowed tabs are never discarded, so the budget can be exceeded when\n * everything left is active/opted-out/vetoed.\n */\n enforceBudget(): number {\n if (this.maxLoadedTabs_ === null) return 0\n let discarded = 0\n const overBudget = () => this.model_.loadedTabCount > this.maxLoadedTabs_!\n if (!overBudget()) return 0\n for (const tab of this.getDiscardCandidates(true)) {\n if (!overBudget()) break\n this.discardTab_(tab)\n discarded++\n }\n return discarded\n }\n\n /**\n * Enforces `exclusiveContentKey`: for every key shared by more than one\n * loaded tab, keep the active tab (else the most recently active) and\n * discard the rest. Returns the number of tabs discarded. Runs before the\n * budget pass, so duplicates count toward freed budget first.\n */\n enforceExclusiveContent(): number {\n if (!this.exclusiveContentKey_) return 0\n const groups = new Map<string, Array<Tab<T>>>()\n for (const tab of this.model_.getTabs()) {\n if (tab.discarded) continue\n const key = this.exclusiveContentKey_(tab)\n if (key === null || key === undefined) continue\n const group = groups.get(key)\n if (group) group.push(tab)\n else groups.set(key, [tab])\n }\n const activeTab = this.model_.activeTab\n let discarded = 0\n for (const tabs of groups.values()) {\n if (tabs.length < 2) continue\n const keep = tabs.includes(activeTab as Tab<T>)\n ? (activeTab as Tab<T>)\n : tabs.reduce((a, b) => (b.lastActiveAt > a.lastActiveAt ? b : a))\n for (const tab of tabs) {\n if (tab === keep) continue\n this.discardTab_(tab)\n discarded++\n }\n }\n return discarded\n }\n\n private discardTab_(tab: Tab<T>): void {\n const index = this.model_.indexOfTab(tab)\n if (index === -1) return\n this.onBeforeDiscard_?.(tab)\n this.model_.discardTabAt(index)\n }\n\n private scheduleEnforce_(): void {\n if (this.enforcePending_) return\n this.enforcePending_ = true\n queueMicrotask(() => {\n this.enforcePending_ = false\n if (this.detach_) {\n this.enforceExclusiveContent()\n this.enforceBudget()\n }\n })\n }\n}\n","import { useMemo, useRef, useSyncExternalStore } from 'react'\nimport type { Tab, TabGroup, TabStripModelOptions } from '../core/types'\nimport { TabStripModel } from '../core/tab-strip-model'\n\nexport interface TabStripSnapshot<T> {\n tabs: ReadonlyArray<Tab<T>>\n activeTab: Tab<T> | null\n activeIndex: number\n selectedIndices: ReadonlyArray<number>\n groups: ReadonlyArray<TabGroup>\n}\n\n/** Creates a TabStripModel once for the component's lifetime. */\nexport function useTabStripModel<T>(\n init?: (model: TabStripModel<T>) => void,\n options?: TabStripModelOptions<T>,\n): TabStripModel<T> {\n const ref = useRef<TabStripModel<T> | null>(null)\n if (ref.current === null) {\n ref.current = new TabStripModel<T>(options)\n init?.(ref.current)\n }\n return ref.current\n}\n\n/**\n * Subscribes a component to a TabStripModel. Re-renders on any model change\n * (the model batches per-operation, so one operation is one render).\n */\nexport function useTabStrip<T>(model: TabStripModel<T>): TabStripSnapshot<T> {\n const store = useMemo(() => {\n let version = 0\n let snapshotVersion = -1\n let snapshot: TabStripSnapshot<T> | null = null\n return {\n subscribe(onStoreChange: () => void): () => void {\n return model.addObserver({\n onTabStripModelChanged: () => {\n version++\n onStoreChange()\n },\n onTabPinnedStateChanged: () => {\n version++\n onStoreChange()\n },\n onTabGroupedStateChanged: () => {\n version++\n onStoreChange()\n },\n onTabGroupChanged: () => {\n version++\n onStoreChange()\n },\n onTabChanged: () => {\n version++\n onStoreChange()\n },\n onTabDiscardedStateChanged: () => {\n version++\n onStoreChange()\n },\n })\n },\n getSnapshot(): TabStripSnapshot<T> {\n if (snapshot === null || snapshotVersion !== version) {\n snapshot = {\n tabs: model.getTabs(),\n activeTab: model.activeTab,\n activeIndex: model.activeIndex,\n selectedIndices: model.selectionModel().selectedIndices(),\n groups: model.getGroups(),\n }\n snapshotVersion = version\n }\n return snapshot\n },\n }\n }, [model])\n\n return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)\n}\n","import { useCallback, useRef, useState, type PointerEvent, type RefObject } from 'react'\nimport type { TabStripModel } from '../core/tab-strip-model'\n\nconst DRAG_THRESHOLD_PX = 4\n\n/**\n * Pointer-based drag-to-reorder. On drag, the hovered insertion position is\n * computed from the midpoints of the rendered tabs and the model's moveTabTo\n * applies Chrome's clamping (pinned boundary) and group-assignment rules.\n */\nexport function useTabDrag<T>(\n model: TabStripModel<T>,\n containerRef: RefObject<HTMLElement | null>,\n): {\n draggingTabId: string | null\n onTabPointerDown: (event: PointerEvent, tabId: string) => void\n} {\n const [draggingTabId, setDraggingTabId] = useState<string | null>(null)\n const drag = useRef<{ tabId: string; startX: number; started: boolean } | null>(null)\n\n const onTabPointerDown = useCallback(\n (event: PointerEvent, tabId: string) => {\n if (event.button !== 0) return\n drag.current = { tabId, startX: event.clientX, started: false }\n const target = event.currentTarget as HTMLElement\n target.setPointerCapture(event.pointerId)\n\n const onMove = (e: globalThis.PointerEvent) => {\n const state = drag.current\n if (!state) return\n if (!state.started) {\n if (Math.abs(e.clientX - state.startX) < DRAG_THRESHOLD_PX) return\n state.started = true\n setDraggingTabId(state.tabId)\n }\n const container = containerRef.current\n if (!container) return\n\n const tab = model.getTabById(state.tabId)\n if (!tab) return\n const currentIndex = model.indexOfTab(tab)\n\n // Insertion position: how many other tabs' midpoints are left of the\n // pointer.\n const elements = [...container.querySelectorAll<HTMLElement>('[data-tab-id]')]\n let targetIndex = 0\n for (const el of elements) {\n if (el.dataset['tabId'] === state.tabId) continue\n const rect = el.getBoundingClientRect()\n if (e.clientX > rect.left + rect.width / 2) targetIndex++\n }\n if (targetIndex !== currentIndex) {\n model.moveTabTo(currentIndex, targetIndex)\n }\n }\n\n const onUp = () => {\n drag.current = null\n setDraggingTabId(null)\n target.removeEventListener('pointermove', onMove)\n target.removeEventListener('pointerup', onUp)\n target.removeEventListener('pointercancel', onUp)\n }\n\n target.addEventListener('pointermove', onMove)\n target.addEventListener('pointerup', onUp)\n target.addEventListener('pointercancel', onUp)\n },\n [model, containerRef],\n )\n\n return { draggingTabId, onTabPointerDown }\n}\n","import { createContext, useContext, type CSSProperties, type ReactNode } from 'react'\nimport type { TabStripModel } from '../core/tab-strip-model'\nimport type { Tab } from '../core/types'\nimport { useTabStrip } from './use-tab-strip'\n\nexport type TabVisibility = 'visible' | 'hidden'\n\nconst TabVisibilityContext = createContext<TabVisibility>('visible')\n\n/**\n * Visibility of the enclosing tab panel. The React analogue of the page\n * visibility signal Chrome sends background tabs (WasShown/WasHidden): use it\n * to pause polling, animations, or media while the tab is in the background.\n */\nexport function useTabVisibility(): TabVisibility {\n return useContext(TabVisibilityContext)\n}\n\nexport interface TabPanelsProps<T> {\n model: TabStripModel<T>\n /** Renders a tab's content. Mounted once, kept alive while hidden. */\n children: (tab: Tab<T>) => ReactNode\n className?: string\n /**\n * Hiding strategy for inactive panels. 'display-none' (default) removes\n * hidden panels from layout. 'visibility' keeps layout (useful when\n * content measures itself and must not collapse to zero size).\n */\n hideMode?: 'display-none' | 'visibility'\n}\n\n/**\n * The content host: the React analogue of Chrome keeping background tabs'\n * pages alive. Every non-discarded tab's content stays mounted (component\n * state survives tab switches); only the active tab is visible. Discarded\n * tabs render nothing, and remount fresh when activated, which is exactly\n * Chrome's discard + reload-on-focus lifecycle.\n *\n * Panels are keyed by tab id, so reordering tabs never remounts content.\n */\nexport function TabPanels<T>({ model, children, className, hideMode = 'display-none' }: TabPanelsProps<T>) {\n const snapshot = useTabStrip(model)\n\n return (\n <div className={['ctabs-panels', className].filter(Boolean).join(' ')}>\n {snapshot.tabs.map((tab) => {\n if (tab.discarded) return null\n const visible = tab === snapshot.activeTab\n const style: CSSProperties =\n hideMode === 'display-none'\n ? { display: visible ? undefined : 'none' }\n : {\n visibility: visible ? undefined : 'hidden',\n position: visible ? undefined : 'absolute',\n inset: visible ? undefined : 0,\n }\n return (\n <div\n key={tab.id}\n role=\"tabpanel\"\n data-tab-panel-id={tab.id}\n className=\"ctabs-panel\"\n style={style}\n >\n <TabVisibilityContext.Provider value={visible ? 'visible' : 'hidden'}>\n {children(tab)}\n </TabVisibilityContext.Provider>\n </div>\n )\n })}\n </div>\n )\n}\n","import { useRef, type KeyboardEvent, type MouseEvent, type ReactNode } from 'react'\nimport type { TabStripModel } from '../core/tab-strip-model'\nimport type { Tab } from '../core/types'\nimport { GroupHeader, GROUP_COLOR_VALUES } from './group-header'\nimport { TabItem } from './tab-item'\nimport { useTabDrag } from './use-tab-drag'\nimport { useTabStrip } from './use-tab-strip'\n\nexport interface TabStripProps<T> {\n model: TabStripModel<T>\n /** Renders a tab's content. Defaults to String(tab.data). */\n renderTab?: (tab: Tab<T>) => ReactNode\n /** Shows the new-tab button and handles clicks on it. */\n onNewTab?: () => void\n onTabContextMenu?: (index: number, event: MouseEvent) => void\n className?: string\n}\n\n/**\n * A Chrome-style tab strip bound to a TabStripModel. Click activates,\n * ctrl/cmd-click toggles selection, shift-click extends from the anchor,\n * middle-click closes, dragging reorders, arrow keys switch tabs and\n * ctrl/cmd+arrows move the active tab (hopping group boundaries like Chrome).\n */\nexport function TabStrip<T>({\n model,\n renderTab = (tab) => String(tab.data),\n onNewTab,\n onTabContextMenu,\n className,\n}: TabStripProps<T>) {\n const snapshot = useTabStrip(model)\n const containerRef = useRef<HTMLDivElement | null>(null)\n const { draggingTabId, onTabPointerDown } = useTabDrag(model, containerRef)\n\n const onActivate = (index: number, event: MouseEvent) => {\n if (event.shiftKey) {\n model.extendSelectionTo(index)\n } else if (event.metaKey || event.ctrlKey) {\n if (model.isTabSelected(index)) model.deselectTabAt(index)\n else model.selectTabAt(index)\n } else {\n model.activateTabAt(index, { userGesture: true })\n }\n }\n\n const onKeyDown = (event: KeyboardEvent) => {\n const move = event.metaKey || event.ctrlKey\n if (event.key === 'ArrowRight') {\n move ? model.moveTabNext() : model.selectNextTab({ userGesture: true })\n event.preventDefault()\n } else if (event.key === 'ArrowLeft') {\n move ? model.moveTabPrevious() : model.selectPreviousTab({ userGesture: true })\n event.preventDefault()\n }\n }\n\n const selected = new Set(snapshot.selectedIndices)\n const groupById = new Map(snapshot.groups.map((g) => [g.id, g]))\n const items: ReactNode[] = []\n let previousGroup: string | null = null\n\n snapshot.tabs.forEach((tab, index) => {\n if (tab.group !== null && tab.group !== previousGroup) {\n const group = groupById.get(tab.group)\n if (group) {\n items.push(\n <GroupHeader\n key={`group-${group.id}`}\n group={group}\n onToggleCollapsed={(id) => model.setGroupCollapsed(id, !model.isGroupCollapsed(id))}\n />,\n )\n }\n }\n previousGroup = tab.group\n\n if (tab.group !== null && model.isGroupCollapsed(tab.group)) return\n\n const groupColor = tab.group\n ? (GROUP_COLOR_VALUES[groupById.get(tab.group)?.visualData.color ?? 'grey'] ?? null)\n : null\n\n items.push(\n <TabItem\n key={tab.id}\n tab={tab}\n index={index}\n active={index === snapshot.activeIndex}\n selected={selected.has(index)}\n dragging={tab.id === draggingTabId}\n groupColor={groupColor}\n renderContent={renderTab}\n onPointerDown={onTabPointerDown}\n onActivate={onActivate}\n onClose={(i) => model.closeTabAt(i)}\n onContextMenu={onTabContextMenu}\n />,\n )\n })\n\n return (\n <div\n ref={containerRef}\n role=\"tablist\"\n tabIndex={0}\n className={['ctabs-strip', className].filter(Boolean).join(' ')}\n onKeyDown={onKeyDown}\n >\n {items}\n {onNewTab && (\n <button\n type=\"button\"\n className=\"ctabs-new-tab\"\n aria-label=\"New tab\"\n onClick={onNewTab}\n >\n +\n </button>\n )}\n </div>\n )\n}\n","import type { TabGroup } from '../core/types'\n\nexport const GROUP_COLOR_VALUES: Record<string, string> = {\n grey: '#5f6368',\n blue: '#1a73e8',\n red: '#d93025',\n yellow: '#f9ab00',\n green: '#188038',\n pink: '#d01884',\n purple: '#a142f4',\n cyan: '#007b83',\n orange: '#fa903e',\n}\n\nexport interface GroupHeaderProps {\n group: TabGroup\n onToggleCollapsed: (groupId: string) => void\n}\n\n/** The colored group chip shown before a group's tabs, like Chrome's. */\nexport function GroupHeader({ group, onToggleCollapsed }: GroupHeaderProps) {\n const color = GROUP_COLOR_VALUES[group.visualData.color] ?? GROUP_COLOR_VALUES['grey']!\n return (\n <button\n type=\"button\"\n className={[\n 'ctabs-group-header',\n group.visualData.isCollapsed && 'ctabs-group-header--collapsed',\n ]\n .filter(Boolean)\n .join(' ')}\n style={{ ['--ctabs-group-color' as string]: color }}\n onClick={() => onToggleCollapsed(group.id)}\n title={group.visualData.isCollapsed ? 'Expand group' : 'Collapse group'}\n >\n {group.visualData.title || ' '}\n </button>\n )\n}\n","import type { MouseEvent, PointerEvent, ReactNode } from 'react'\nimport type { Tab } from '../core/types'\n\nexport interface TabItemProps<T> {\n tab: Tab<T>\n index: number\n active: boolean\n selected: boolean\n dragging: boolean\n groupColor: string | null\n renderContent: (tab: Tab<T>) => ReactNode\n onPointerDown: (event: PointerEvent, tabId: string) => void\n onActivate: (index: number, event: MouseEvent) => void\n onClose: (index: number) => void\n onContextMenu?: (index: number, event: MouseEvent) => void\n}\n\nexport function TabItem<T>({\n tab,\n index,\n active,\n selected,\n dragging,\n groupColor,\n renderContent,\n onPointerDown,\n onActivate,\n onClose,\n onContextMenu,\n}: TabItemProps<T>) {\n return (\n <div\n role=\"tab\"\n aria-selected={active}\n data-tab-id={tab.id}\n className={[\n 'ctabs-tab',\n active && 'ctabs-tab--active',\n selected && !active && 'ctabs-tab--selected',\n tab.pinned && 'ctabs-tab--pinned',\n tab.discarded && 'ctabs-tab--discarded',\n dragging && 'ctabs-tab--dragging',\n groupColor && 'ctabs-tab--grouped',\n ]\n .filter(Boolean)\n .join(' ')}\n style={groupColor ? { ['--ctabs-group-color' as string]: groupColor } : undefined}\n onPointerDown={(e) => onPointerDown(e, tab.id)}\n onMouseDown={(e) => {\n // Middle click closes, like Chrome.\n if (e.button === 1) {\n e.preventDefault()\n onClose(index)\n }\n }}\n onClick={(e) => onActivate(index, e)}\n onContextMenu={(e) => onContextMenu?.(index, e)}\n >\n <span className=\"ctabs-tab__content\">{renderContent(tab)}</span>\n {!tab.pinned && (\n <button\n type=\"button\"\n className=\"ctabs-tab__close\"\n aria-label=\"Close tab\"\n onClick={(e) => {\n e.stopPropagation()\n onClose(index)\n }}\n onPointerDown={(e) => e.stopPropagation()}\n >\n ×\n </button>\n )}\n </div>\n )\n}\n","import type { MouseEvent, ReactNode } from 'react'\nimport type { TabStripModel } from '../core/tab-strip-model'\nimport type { Tab } from '../core/types'\nimport { TabPanels } from './tab-panels'\nimport { TabStrip } from './tab-strip'\n\nexport interface TabsProps<T> {\n model: TabStripModel<T>\n /** Renders a tab's strip label. Defaults to String(tab.data). */\n renderTab?: (tab: Tab<T>) => ReactNode\n /**\n * Renders a tab's content. Hosted in TabPanels: mounted once, kept alive\n * while the tab is in the background, unmounted only on discard.\n */\n children: (tab: Tab<T>) => ReactNode\n onNewTab?: () => void\n onTabContextMenu?: (index: number, event: MouseEvent) => void\n /** Panel hiding strategy, see TabPanels. */\n hideMode?: 'display-none' | 'visibility'\n className?: string\n}\n\n/**\n * The batteries-included layout: strip on top, keep-alive content below.\n * This is the recommended entry point — content state survives tab switches\n * by construction, because the panels host every loaded tab's tree like\n * Chrome keeps background pages alive.\n *\n * Use the composable pieces (TabStrip, TabPanels) directly only when you\n * need a custom layout, and keep content inside TabPanels unless you\n * specifically want remount-on-switch semantics.\n */\nexport function Tabs<T>({\n model,\n renderTab,\n children,\n onNewTab,\n onTabContextMenu,\n hideMode,\n className,\n}: TabsProps<T>) {\n return (\n <div className={['ctabs', className].filter(Boolean).join(' ')}>\n <TabStrip\n model={model}\n renderTab={renderTab}\n onNewTab={onNewTab}\n onTabContextMenu={onTabContextMenu}\n />\n <TabPanels model={model} hideMode={hideMode}>\n {children}\n </TabPanels>\n </div>\n )\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime"]}
@@ -0,0 +1,128 @@
1
+ import { Tab, TabGroup, TabStripModel, TabStripModelOptions } from './core/index.cjs';
2
+ export { AddTabFlags, AddTabOptions, CanDiscardDecision, CanDiscardResult, CannotDiscardReason, CloseAllStoppedReason, CloseTabFlags, DiscardReason, IndexRange, ListSelectionModel, NO_TAB, ReconcileOptions, ReconcileTab, TAB_GROUP_COLORS, TabGroupChange, TabGroupColor, TabGroupId, TabGroupVisualData, TabId, TabLifecycleManager, TabLifecycleOptions, TabOpenCause, TabStripModelChange, TabStripModelObserver, TabStripSelectionChange } from './core/index.cjs';
3
+ import * as react from 'react';
4
+ import { RefObject, PointerEvent, ReactNode, MouseEvent } from 'react';
5
+
6
+ interface TabStripSnapshot<T> {
7
+ tabs: ReadonlyArray<Tab<T>>;
8
+ activeTab: Tab<T> | null;
9
+ activeIndex: number;
10
+ selectedIndices: ReadonlyArray<number>;
11
+ groups: ReadonlyArray<TabGroup>;
12
+ }
13
+ /** Creates a TabStripModel once for the component's lifetime. */
14
+ declare function useTabStripModel<T>(init?: (model: TabStripModel<T>) => void, options?: TabStripModelOptions<T>): TabStripModel<T>;
15
+ /**
16
+ * Subscribes a component to a TabStripModel. Re-renders on any model change
17
+ * (the model batches per-operation, so one operation is one render).
18
+ */
19
+ declare function useTabStrip<T>(model: TabStripModel<T>): TabStripSnapshot<T>;
20
+
21
+ /**
22
+ * Pointer-based drag-to-reorder. On drag, the hovered insertion position is
23
+ * computed from the midpoints of the rendered tabs and the model's moveTabTo
24
+ * applies Chrome's clamping (pinned boundary) and group-assignment rules.
25
+ */
26
+ declare function useTabDrag<T>(model: TabStripModel<T>, containerRef: RefObject<HTMLElement | null>): {
27
+ draggingTabId: string | null;
28
+ onTabPointerDown: (event: PointerEvent, tabId: string) => void;
29
+ };
30
+
31
+ interface TabsProps<T> {
32
+ model: TabStripModel<T>;
33
+ /** Renders a tab's strip label. Defaults to String(tab.data). */
34
+ renderTab?: (tab: Tab<T>) => ReactNode;
35
+ /**
36
+ * Renders a tab's content. Hosted in TabPanels: mounted once, kept alive
37
+ * while the tab is in the background, unmounted only on discard.
38
+ */
39
+ children: (tab: Tab<T>) => ReactNode;
40
+ onNewTab?: () => void;
41
+ onTabContextMenu?: (index: number, event: MouseEvent) => void;
42
+ /** Panel hiding strategy, see TabPanels. */
43
+ hideMode?: 'display-none' | 'visibility';
44
+ className?: string;
45
+ }
46
+ /**
47
+ * The batteries-included layout: strip on top, keep-alive content below.
48
+ * This is the recommended entry point — content state survives tab switches
49
+ * by construction, because the panels host every loaded tab's tree like
50
+ * Chrome keeps background pages alive.
51
+ *
52
+ * Use the composable pieces (TabStrip, TabPanels) directly only when you
53
+ * need a custom layout, and keep content inside TabPanels unless you
54
+ * specifically want remount-on-switch semantics.
55
+ */
56
+ declare function Tabs<T>({ model, renderTab, children, onNewTab, onTabContextMenu, hideMode, className, }: TabsProps<T>): react.JSX.Element;
57
+
58
+ interface TabStripProps<T> {
59
+ model: TabStripModel<T>;
60
+ /** Renders a tab's content. Defaults to String(tab.data). */
61
+ renderTab?: (tab: Tab<T>) => ReactNode;
62
+ /** Shows the new-tab button and handles clicks on it. */
63
+ onNewTab?: () => void;
64
+ onTabContextMenu?: (index: number, event: MouseEvent) => void;
65
+ className?: string;
66
+ }
67
+ /**
68
+ * A Chrome-style tab strip bound to a TabStripModel. Click activates,
69
+ * ctrl/cmd-click toggles selection, shift-click extends from the anchor,
70
+ * middle-click closes, dragging reorders, arrow keys switch tabs and
71
+ * ctrl/cmd+arrows move the active tab (hopping group boundaries like Chrome).
72
+ */
73
+ declare function TabStrip<T>({ model, renderTab, onNewTab, onTabContextMenu, className, }: TabStripProps<T>): react.JSX.Element;
74
+
75
+ type TabVisibility = 'visible' | 'hidden';
76
+ /**
77
+ * Visibility of the enclosing tab panel. The React analogue of the page
78
+ * visibility signal Chrome sends background tabs (WasShown/WasHidden): use it
79
+ * to pause polling, animations, or media while the tab is in the background.
80
+ */
81
+ declare function useTabVisibility(): TabVisibility;
82
+ interface TabPanelsProps<T> {
83
+ model: TabStripModel<T>;
84
+ /** Renders a tab's content. Mounted once, kept alive while hidden. */
85
+ children: (tab: Tab<T>) => ReactNode;
86
+ className?: string;
87
+ /**
88
+ * Hiding strategy for inactive panels. 'display-none' (default) removes
89
+ * hidden panels from layout. 'visibility' keeps layout (useful when
90
+ * content measures itself and must not collapse to zero size).
91
+ */
92
+ hideMode?: 'display-none' | 'visibility';
93
+ }
94
+ /**
95
+ * The content host: the React analogue of Chrome keeping background tabs'
96
+ * pages alive. Every non-discarded tab's content stays mounted (component
97
+ * state survives tab switches); only the active tab is visible. Discarded
98
+ * tabs render nothing, and remount fresh when activated, which is exactly
99
+ * Chrome's discard + reload-on-focus lifecycle.
100
+ *
101
+ * Panels are keyed by tab id, so reordering tabs never remounts content.
102
+ */
103
+ declare function TabPanels<T>({ model, children, className, hideMode }: TabPanelsProps<T>): react.JSX.Element;
104
+
105
+ interface TabItemProps<T> {
106
+ tab: Tab<T>;
107
+ index: number;
108
+ active: boolean;
109
+ selected: boolean;
110
+ dragging: boolean;
111
+ groupColor: string | null;
112
+ renderContent: (tab: Tab<T>) => ReactNode;
113
+ onPointerDown: (event: PointerEvent, tabId: string) => void;
114
+ onActivate: (index: number, event: MouseEvent) => void;
115
+ onClose: (index: number) => void;
116
+ onContextMenu?: (index: number, event: MouseEvent) => void;
117
+ }
118
+ declare function TabItem<T>({ tab, index, active, selected, dragging, groupColor, renderContent, onPointerDown, onActivate, onClose, onContextMenu, }: TabItemProps<T>): react.JSX.Element;
119
+
120
+ declare const GROUP_COLOR_VALUES: Record<string, string>;
121
+ interface GroupHeaderProps {
122
+ group: TabGroup;
123
+ onToggleCollapsed: (groupId: string) => void;
124
+ }
125
+ /** The colored group chip shown before a group's tabs, like Chrome's. */
126
+ declare function GroupHeader({ group, onToggleCollapsed }: GroupHeaderProps): react.JSX.Element;
127
+
128
+ export { GROUP_COLOR_VALUES, GroupHeader, type GroupHeaderProps, Tab, TabGroup, TabItem, type TabItemProps, TabPanels, type TabPanelsProps, TabStrip, TabStripModel, TabStripModelOptions, type TabStripProps, type TabStripSnapshot, type TabVisibility, Tabs, type TabsProps, useTabDrag, useTabStrip, useTabStripModel, useTabVisibility };
@@ -0,0 +1,128 @@
1
+ import { Tab, TabGroup, TabStripModel, TabStripModelOptions } from './core/index.js';
2
+ export { AddTabFlags, AddTabOptions, CanDiscardDecision, CanDiscardResult, CannotDiscardReason, CloseAllStoppedReason, CloseTabFlags, DiscardReason, IndexRange, ListSelectionModel, NO_TAB, ReconcileOptions, ReconcileTab, TAB_GROUP_COLORS, TabGroupChange, TabGroupColor, TabGroupId, TabGroupVisualData, TabId, TabLifecycleManager, TabLifecycleOptions, TabOpenCause, TabStripModelChange, TabStripModelObserver, TabStripSelectionChange } from './core/index.js';
3
+ import * as react from 'react';
4
+ import { RefObject, PointerEvent, ReactNode, MouseEvent } from 'react';
5
+
6
+ interface TabStripSnapshot<T> {
7
+ tabs: ReadonlyArray<Tab<T>>;
8
+ activeTab: Tab<T> | null;
9
+ activeIndex: number;
10
+ selectedIndices: ReadonlyArray<number>;
11
+ groups: ReadonlyArray<TabGroup>;
12
+ }
13
+ /** Creates a TabStripModel once for the component's lifetime. */
14
+ declare function useTabStripModel<T>(init?: (model: TabStripModel<T>) => void, options?: TabStripModelOptions<T>): TabStripModel<T>;
15
+ /**
16
+ * Subscribes a component to a TabStripModel. Re-renders on any model change
17
+ * (the model batches per-operation, so one operation is one render).
18
+ */
19
+ declare function useTabStrip<T>(model: TabStripModel<T>): TabStripSnapshot<T>;
20
+
21
+ /**
22
+ * Pointer-based drag-to-reorder. On drag, the hovered insertion position is
23
+ * computed from the midpoints of the rendered tabs and the model's moveTabTo
24
+ * applies Chrome's clamping (pinned boundary) and group-assignment rules.
25
+ */
26
+ declare function useTabDrag<T>(model: TabStripModel<T>, containerRef: RefObject<HTMLElement | null>): {
27
+ draggingTabId: string | null;
28
+ onTabPointerDown: (event: PointerEvent, tabId: string) => void;
29
+ };
30
+
31
+ interface TabsProps<T> {
32
+ model: TabStripModel<T>;
33
+ /** Renders a tab's strip label. Defaults to String(tab.data). */
34
+ renderTab?: (tab: Tab<T>) => ReactNode;
35
+ /**
36
+ * Renders a tab's content. Hosted in TabPanels: mounted once, kept alive
37
+ * while the tab is in the background, unmounted only on discard.
38
+ */
39
+ children: (tab: Tab<T>) => ReactNode;
40
+ onNewTab?: () => void;
41
+ onTabContextMenu?: (index: number, event: MouseEvent) => void;
42
+ /** Panel hiding strategy, see TabPanels. */
43
+ hideMode?: 'display-none' | 'visibility';
44
+ className?: string;
45
+ }
46
+ /**
47
+ * The batteries-included layout: strip on top, keep-alive content below.
48
+ * This is the recommended entry point — content state survives tab switches
49
+ * by construction, because the panels host every loaded tab's tree like
50
+ * Chrome keeps background pages alive.
51
+ *
52
+ * Use the composable pieces (TabStrip, TabPanels) directly only when you
53
+ * need a custom layout, and keep content inside TabPanels unless you
54
+ * specifically want remount-on-switch semantics.
55
+ */
56
+ declare function Tabs<T>({ model, renderTab, children, onNewTab, onTabContextMenu, hideMode, className, }: TabsProps<T>): react.JSX.Element;
57
+
58
+ interface TabStripProps<T> {
59
+ model: TabStripModel<T>;
60
+ /** Renders a tab's content. Defaults to String(tab.data). */
61
+ renderTab?: (tab: Tab<T>) => ReactNode;
62
+ /** Shows the new-tab button and handles clicks on it. */
63
+ onNewTab?: () => void;
64
+ onTabContextMenu?: (index: number, event: MouseEvent) => void;
65
+ className?: string;
66
+ }
67
+ /**
68
+ * A Chrome-style tab strip bound to a TabStripModel. Click activates,
69
+ * ctrl/cmd-click toggles selection, shift-click extends from the anchor,
70
+ * middle-click closes, dragging reorders, arrow keys switch tabs and
71
+ * ctrl/cmd+arrows move the active tab (hopping group boundaries like Chrome).
72
+ */
73
+ declare function TabStrip<T>({ model, renderTab, onNewTab, onTabContextMenu, className, }: TabStripProps<T>): react.JSX.Element;
74
+
75
+ type TabVisibility = 'visible' | 'hidden';
76
+ /**
77
+ * Visibility of the enclosing tab panel. The React analogue of the page
78
+ * visibility signal Chrome sends background tabs (WasShown/WasHidden): use it
79
+ * to pause polling, animations, or media while the tab is in the background.
80
+ */
81
+ declare function useTabVisibility(): TabVisibility;
82
+ interface TabPanelsProps<T> {
83
+ model: TabStripModel<T>;
84
+ /** Renders a tab's content. Mounted once, kept alive while hidden. */
85
+ children: (tab: Tab<T>) => ReactNode;
86
+ className?: string;
87
+ /**
88
+ * Hiding strategy for inactive panels. 'display-none' (default) removes
89
+ * hidden panels from layout. 'visibility' keeps layout (useful when
90
+ * content measures itself and must not collapse to zero size).
91
+ */
92
+ hideMode?: 'display-none' | 'visibility';
93
+ }
94
+ /**
95
+ * The content host: the React analogue of Chrome keeping background tabs'
96
+ * pages alive. Every non-discarded tab's content stays mounted (component
97
+ * state survives tab switches); only the active tab is visible. Discarded
98
+ * tabs render nothing, and remount fresh when activated, which is exactly
99
+ * Chrome's discard + reload-on-focus lifecycle.
100
+ *
101
+ * Panels are keyed by tab id, so reordering tabs never remounts content.
102
+ */
103
+ declare function TabPanels<T>({ model, children, className, hideMode }: TabPanelsProps<T>): react.JSX.Element;
104
+
105
+ interface TabItemProps<T> {
106
+ tab: Tab<T>;
107
+ index: number;
108
+ active: boolean;
109
+ selected: boolean;
110
+ dragging: boolean;
111
+ groupColor: string | null;
112
+ renderContent: (tab: Tab<T>) => ReactNode;
113
+ onPointerDown: (event: PointerEvent, tabId: string) => void;
114
+ onActivate: (index: number, event: MouseEvent) => void;
115
+ onClose: (index: number) => void;
116
+ onContextMenu?: (index: number, event: MouseEvent) => void;
117
+ }
118
+ declare function TabItem<T>({ tab, index, active, selected, dragging, groupColor, renderContent, onPointerDown, onActivate, onClose, onContextMenu, }: TabItemProps<T>): react.JSX.Element;
119
+
120
+ declare const GROUP_COLOR_VALUES: Record<string, string>;
121
+ interface GroupHeaderProps {
122
+ group: TabGroup;
123
+ onToggleCollapsed: (groupId: string) => void;
124
+ }
125
+ /** The colored group chip shown before a group's tabs, like Chrome's. */
126
+ declare function GroupHeader({ group, onToggleCollapsed }: GroupHeaderProps): react.JSX.Element;
127
+
128
+ export { GROUP_COLOR_VALUES, GroupHeader, type GroupHeaderProps, Tab, TabGroup, TabItem, type TabItemProps, TabPanels, type TabPanelsProps, TabStrip, TabStripModel, TabStripModelOptions, type TabStripProps, type TabStripSnapshot, type TabVisibility, Tabs, type TabsProps, useTabDrag, useTabStrip, useTabStripModel, useTabVisibility };