Files
v-iole-t--tab-harbor/extension/index.html
T
2026-07-13 12:34:04 +08:00

387 lines
20 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tab Harbor</title>
<!-- Google Fonts: Libre Caslon Display (editorial display) + Public Sans (UI body) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link
href="https://fonts.googleapis.com/css2?family=Libre+Caslon+Display&family=Public+Sans:wght@400;500;600;700&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<div class="group-nav group-nav-wide" id="workspaceTopNav" style="display:none"></div>
<main class="workspace-page is-active" id="homePage" data-workspace-page="home">
<!-- ================================================================
TAB HARBOR DUPES BANNER — appears when you have extra Tab Harbor tabs open
Hidden by default; app.js shows it when dupeTabOutCount > 1
================================================================ -->
<div class="tab-cleanup-banner" id="tabOutDupeBanner" style="display:none">
<div class="tab-cleanup-left">
<div class="tab-cleanup-icon">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75" />
</svg>
</div>
<div class="tab-cleanup-text">
You have <strong id="tabOutDupeCount"></strong> Tab Harbor tabs open. Keep just this one?
</div>
</div>
<button class="tab-cleanup-btn" data-action="close-tabout-dupes">Close extras</button>
</div>
<!-- ================================================================
MAIN CONTENT AREA — left column for open tabs, right rail for
greeting/date + drawer controls
================================================================ -->
<div class="dashboard-columns" id="dashboardColumns">
<!-- LEFT COLUMN: Open tabs -->
<div class="active-section" id="openTabsSection" style="display:none">
<div class="section-header">
<h2 id="openTabsSectionTitle">Right now</h2>
<div class="section-line"></div>
<div class="section-count" id="openTabsSectionCount"></div>
</div>
<div class="missions" id="openTabsMissions"></div>
</div>
<!-- RIGHT RAIL: greeting + date -->
<header class="page-header">
<div class="header-left">
<div class="header-title-row">
<!-- "Good afternoon" — written by getGreeting() in app.js -->
<h1 id="greeting"></h1>
<!-- "Friday, April 4, 2026" — written by getDateDisplay() in app.js -->
<div class="date" id="dateDisplay"></div>
</div>
<div class="hitokoto" id="hitokoto" style="display:none" aria-live="polite" role="note">
<span class="hitokoto-text" id="hitokotoText"></span>
<span class="hitokoto-from" id="hitokotoFrom"></span>
</div>
<form class="header-search" id="headerSearchForm">
<label class="header-search-label" for="headerSearchInput">Search the web</label>
<div class="header-search-shell">
<svg class="header-search-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.8" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round"
d="m21 21-4.35-4.35m1.85-5.15a7 7 0 1 1-14 0 7 7 0 0 1 14 0Z" />
</svg>
<input class="header-search-input" id="headerSearchInput" type="search" name="q"
placeholder="Search with your default engine..." autocomplete="off" spellcheck="false">
</div>
</form>
<section class="quick-tabs" id="quickTabsSection">
<div class="quick-tabs-grid" id="quickTabsList"></div>
</section>
</div>
</header>
</div><!-- end .dashboard-columns -->
<!-- ================================================================
FOOTER — global stats + refresh control
================================================================ -->
<footer>
<div class="footer-stats">
<div class="stat">
<div class="stat-num" id="statTabs"></div>
<div class="stat-label">Open tabs</div>
</div>
</div>
<div class="last-refresh">
<span class="footer-credit"><a class="footer-credit-link" href="https://github.com/V-IOLE-T/tab-harbor"
target="_top">Tab Harbor</a> by <a class="footer-credit-link" href="https://github.com/V-IOLE-T"
target="_top">OO</a></span>
</div>
</footer>
</main>
<main class="workspace-page" id="savedTabsPage" data-workspace-page="saved-tabs" hidden>
<div class="dashboard-columns saved-tabs-columns">
<section class="active-section saved-sessions-section">
<div class="section-header">
<h2>Saved sessions</h2>
<div class="section-line"></div>
<div class="section-count" id="savedSessionsCount"></div>
</div>
<div class="saved-sessions-list" id="savedSessionsList"></div>
<div class="saved-sessions-empty" id="savedSessionsEmpty" hidden>
No saved sessions yet. Save a window when you want the memory back without losing the trail.
</div>
</section>
<div class="page-header saved-tabs-header-spacer" aria-hidden="true"></div>
</div>
</main>
<div class="drawer-trigger-stack" id="drawerTriggerStack">
<button class="deferred-trigger todo-trigger" id="todoTrigger" type="button" aria-expanded="false"
aria-controls="drawerColumn" aria-label="Open todos">
<svg class="deferred-trigger-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" fill="none"
aria-hidden="true">
<path
d="M288.384 173.488a94.208 94.208 0 0 1 93.392-81.488H646.24a94.208 94.208 0 0 1 93.376 81.488h62.176A94.16 94.16 0 0 1 896 267.568V584a32 32 0 1 1-64 0V267.584c0-16.56-13.472-30.08-30.224-30.08H738.24a94.24 94.24 0 0 1-92.016 73.712H381.76a94.24 94.24 0 0 1-92-73.728h-63.552a30.16 30.16 0 0 0-30.224 30.08V841.92c0 16.56 13.472 30.096 30.224 30.096H516a32 32 0 1 1 0 64H226.224a94.16 94.16 0 0 1-94.224-94.08V267.568c0-52.032 42.24-94.08 94.224-94.08h62.176z m93.392-17.488a30.16 30.16 0 0 0-30.224 30.08v31.04c0 16.56 13.472 30.096 30.24 30.096h264.432a30.16 30.16 0 0 0 30.224-30.08v-31.04c0-16.56-13.472-30.096-30.24-30.096H381.792z"
fill="currentColor" />
<path
d="M472 444a32 32 0 0 1 32-32h236a32 32 0 1 1 0 64H504a32 32 0 0 1-32-32zM472 668a32 32 0 0 1 32-32h192a32 32 0 1 1 0 64h-192a32 32 0 0 1-32-32zM268 396a32 32 0 0 1 32-32h96a32 32 0 0 1 32 32v96a32 32 0 0 1-32 32h-96a32 32 0 0 1-32-32v-96z m64 32v32h32v-32h-32zM268 624a32 32 0 0 1 32-32h96a32 32 0 0 1 32 32v96a32 32 0 0 1-32 32h-96a32 32 0 0 1-32-32v-96z m64 32v32h32v-32h-32zM926.624 660.752a32 32 0 0 1 0 45.248L719.216 913.424a64 64 0 0 1-90.512 0l-85.328-85.344a32 32 0 1 1 45.248-45.248l85.328 85.328 207.424-207.408a32 32 0 0 1 45.248 0z"
fill="currentColor" />
</svg>
</button>
</div>
<div class="deferred-overlay" id="deferredOverlay" hidden></div>
<!-- RIGHT COLUMN: Drawer -->
<aside class="deferred-column" id="drawerColumn" style="display:none" aria-hidden="true" role="dialog"
aria-modal="false" aria-label="Todos" tabindex="-1">
<div class="deferred-shell">
<section class="drawer-panel is-active" id="todoPanel" role="tabpanel" aria-label="Todos" tabindex="-1">
<div class="section-header deferred-header todo-header">
<div class="drawer-title-switch">
<button class="drawer-title-btn is-active" type="button" role="tab" aria-selected="true"
aria-controls="todoPanel" data-view="todos">Todos</button>
</div>
<div class="section-line"></div>
<div class="section-count" id="todoCount">0</div>
<div class="drawer-header-actions todo-header-actions">
<button class="drawer-icon-btn" id="todoSearchToggle" type="button" data-action="toggle-todo-search"
aria-label="Search todos" aria-expanded="false" aria-controls="todoSearchWrap">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="m21 21-4.35-4.35m1.6-5.4a7 7 0 1 1-14 0 7 7 0 0 1 14 0Z" />
</svg>
</button>
<button class="drawer-icon-btn todo-plus-btn" id="todoNewBtn" type="button" data-action="create-todo"
aria-label="New todo">+</button>
<button class="drawer-icon-btn" type="button" data-action="close-drawer" aria-label="Close right drawer">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
<div class="todo-search-wrap" id="todoSearchWrap" style="display:none">
<input type="search" class="archive-search" id="todoSearchInput" placeholder="Search todos..."
aria-label="Search todos">
</div>
<div class="todo-detail-view" id="todoDetailView" style="display:none"></div>
<div class="todo-list" id="todoList"></div>
<div class="deferred-empty" id="todoEmpty" style="display:none">No tasks yet. Capture the next step when
something needs a follow-up.</div>
<div class="deferred-archive" id="todoArchive" style="display:none">
<div class="archive-header-row">
<button class="archive-toggle" id="todoArchiveToggle" type="button" aria-expanded="false"
aria-controls="todoArchiveBody">
<svg class="archive-chevron" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
Archive
<span class="archive-count" id="todoArchiveCount"></span>
</button>
<button class="archive-clear-btn" id="clearTodoArchiveBtn" type="button"
data-action="clear-todo-archive">Clear archive</button>
</div>
<div class="archive-body" id="todoArchiveBody" style="display:none" hidden>
<div class="archive-list" id="todoArchiveList"></div>
</div>
</div>
</section>
</div>
</aside>
</div><!-- end .container -->
<!-- ================================================================
TOAST — floats above everything, used for action confirmations
Hidden by default via CSS (opacity:0, translateY)
================================================================ -->
<div class="toast" id="toast">
<!-- Checkmark circle icon (Heroicons outline) -->
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
<span id="toastText"></span>
</div>
<div class="shortcut-editor-backdrop" id="shortcutEditorBackdrop" hidden></div>
<div class="shortcut-editor" id="shortcutEditor" hidden role="dialog" aria-modal="true"
aria-labelledby="shortcutEditorTitle">
<form class="shortcut-editor-form" id="shortcutEditorForm">
<div class="shortcut-editor-header">
<div class="shortcut-editor-header-main">
<button class="shortcut-editor-back" id="shortcutEditorBack" type="button"
data-action="return-to-tab-picker" aria-label="Back" hidden>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m15.75 19.5-7.5-7.5 7.5-7.5" />
</svg>
</button>
<h2 class="shortcut-editor-title" id="shortcutEditorTitle">Edit shortcut</h2>
</div>
<button class="shortcut-editor-close" type="button" data-action="close-shortcut-editor"
aria-label="Close shortcut editor">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"
aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
</svg>
</button>
</div>
<label class="shortcut-editor-field">
<span class="shortcut-editor-label">URL</span>
<input class="shortcut-editor-input" id="shortcutEditorUrl" name="url" type="url"
placeholder="https://example.com" required>
</label>
<label class="shortcut-editor-field">
<span class="shortcut-editor-label">Label</span>
<input class="shortcut-editor-input" id="shortcutEditorLabel" name="label" type="text"
placeholder="Optional shortcut label">
</label>
<div class="shortcut-editor-section">
<div class="shortcut-editor-source-row">
<div class="shortcut-editor-preview" id="shortcutEditorPreview" tabindex="0"
aria-label="Shortcut icon preview and paste target">
<span class="shortcut-editor-preview-fallback" id="shortcutEditorPreviewFallback">A</span>
</div>
<div class="shortcut-editor-source-segments" id="shortcutEditorSource" role="tablist"
aria-label="Shortcut icon source">
<button class="shortcut-editor-source-chip" type="button" data-action="select-shortcut-source"
data-source="site" aria-pressed="true">Website</button>
<button class="shortcut-editor-source-chip" type="button" data-action="select-shortcut-source"
data-source="glyph" aria-pressed="false">Emoji</button>
<button class="shortcut-editor-source-chip" type="button" data-action="select-shortcut-source"
data-source="image" aria-pressed="false">Image</button>
<button class="shortcut-editor-source-chip" type="button" data-action="select-shortcut-source"
data-source="svg" aria-pressed="false">SVG</button>
</div>
</div>
<div class="shortcut-editor-mode-stage">
<div class="shortcut-editor-mode-group" id="shortcutEditorSiteGroup"></div>
<div class="shortcut-editor-field shortcut-editor-mode-group" id="shortcutEditorEmojiGroup">
<label class="shortcut-editor-inline-field" for="shortcutEditorEmoji">
<span class="shortcut-editor-label">Emoji</span>
<input class="shortcut-editor-input shortcut-editor-emoji-input" id="shortcutEditorEmoji" name="emoji"
type="text" inputmode="text" placeholder="✨" maxlength="4">
</label>
</div>
<div class="shortcut-editor-mode-group" id="shortcutEditorImageGroup">
<div class="shortcut-editor-actions-row">
<button class="theme-menu-action" type="button" data-action="upload-shortcut-icon">Upload image</button>
</div>
<div class="shortcut-editor-mode-note">Paste an image with Cmd/Ctrl+V while the editor is focused.</div>
</div>
<label class="shortcut-editor-field shortcut-editor-mode-group" id="shortcutEditorSvgGroup">
<span class="shortcut-editor-label">SVG code</span>
<textarea class="shortcut-editor-input shortcut-editor-textarea" id="shortcutEditorSvgCode" name="svgCode"
rows="4" placeholder="<svg ...>...</svg>"></textarea>
</label>
</div>
<input type="file" id="shortcutIconFileInput" accept="image/*" hidden>
</div>
<div class="shortcut-editor-footer">
<button class="theme-menu-action is-secondary" type="button" data-action="close-shortcut-editor">Cancel</button>
<button class="theme-menu-action" type="submit">Save</button>
</div>
</form>
</div>
<div class="tab-picker-backdrop" id="tabPickerBackdrop" hidden></div>
<div class="tab-picker" id="tabPicker" hidden role="dialog" aria-label="Add open tabs to quick shortcuts"
aria-modal="true">
<div class="tab-picker-header">
<div class="drawer-title-switch tab-picker-view-switch" id="tabPickerViewSwitch" role="tablist"
aria-label="Add shortcut views">
<button class="drawer-title-btn is-active" id="tabPickerTabsTab" type="button" role="tab"
aria-selected="true" aria-controls="tabPickerTabsPane" data-action="switch-tab-picker-view"
data-view="tabs">Add open tabs</button>
<button class="drawer-title-btn" id="tabPickerUrlTab" type="button" role="tab" aria-selected="false"
aria-controls="tabPickerUrlPane" data-action="switch-tab-picker-view" data-view="url">Add by URL</button>
</div>
<button class="tab-picker-close" type="button" data-action="close-tab-picker" aria-label="Close">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="tab-picker-search-wrap" id="tabPickerSearchWrap">
<svg class="tab-picker-search-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.8" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="m21 21-4.35-4.35m1.6-5.4a7 7 0 1 1-14 0 7 7 0 0 1 14 0Z" />
</svg>
<input class="tab-picker-search" id="tabPickerSearch" type="search" placeholder="Filter tabs..."
autocomplete="off" aria-label="Filter open tabs">
</div>
<div class="tab-picker-list" id="tabPickerList"></div>
<div class="tab-picker-editor-host" id="tabPickerEditorHost" hidden></div>
<div class="tab-picker-footer" id="tabPickerFooter" hidden>
<span class="tab-picker-footer-count" id="tabPickerFooterCount"></span>
<button class="tab-picker-footer-add" type="button" data-action="add-selected-tabs">Add selected</button>
<button class="tab-picker-footer-clear" type="button" data-action="clear-tab-picker-selection">Clear</button>
</div>
</div>
<button class="back-to-top" id="backToTopBtn" type="button" aria-label="Back to top" title="Back to top">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"
aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5" />
</svg>
</button>
<!-- Checked-in defaults + optional local overrides -->
<script src="config.js"></script>
<script src="config-loader.js"></script>
<!-- Shared icon helpers — real favicons first, then graceful fallbacks -->
<script src="icon-utils.js"></script>
<script src="session-groups.js"></script>
<script src="group-order.js"></script>
<script src="deferred-trigger-position.js"></script>
<script src="todos-store.js"></script>
<script src="list-order.js"></script>
<script src="background-image.js"></script>
<script src="i18n.js"></script>
<script src="ui-helpers.js"></script>
<script src="tab-url-utils.js"></script>
<script src="tab-sessions.js"></script>
<script src="session-manager.js"></script>
<script src="theme-controls.js"></script>
<script src="drawer-manager.js"></script>
<script src="chrome-tab-groups-import.js"></script>
<script src="chrome-tab-groups-sync.js"></script>
<script src="dashboard-runtime.js"></script>
<!-- Main dashboard logic — loads last so the DOM is ready -->
<script src="app.js"></script>
</body>
</html>