@oar-sh/oar 0.9.7 → 0.9.9
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.
- package/CHANGELOG.md +344 -0
- package/README.md +89 -40
- package/bin/oar.js +1023 -135
- package/package.json +4 -5
- package/server/README.md +20 -1
- package/server/claude-worker/claude-sdk-adapter.mjs +0 -11
- package/server/claude-worker/claude-session-process.mjs +196 -28
- package/server/claude-worker/sdk-message-normalizer.mjs +28 -0
- package/server/db-schema.mjs +8 -0
- package/server/migrations/0008-message-pins.mjs +85 -0
- package/server/public/app/activity-replay-state.mjs +68 -5
- package/server/public/app/api-client.js +36 -2
- package/server/public/app/attachments-view.js +508 -125
- package/server/public/app/back-guard.mjs +58 -0
- package/server/public/app/bootstrap.js +170 -59
- package/server/public/app/context-usage-view.mjs +127 -8
- package/server/public/app/conversation-view.js +172 -32
- package/server/public/app/external-link-policy.mjs +4 -1
- package/server/public/app/file-version.mjs +58 -0
- package/server/public/app/journal-view.js +16 -2
- package/server/public/app/pinned-messages-view.mjs +253 -0
- package/server/public/app/pwa-install.js +6 -1
- package/server/public/app/relay-dot-state.mjs +8 -1
- package/server/public/app/router.js +8 -2
- package/server/public/app/scrollbar-hover.mjs +78 -0
- package/server/public/app/settings-modal.js +4 -1
- package/server/public/app/socket-handlers.js +8 -1
- package/server/public/app/store.js +32 -0
- package/server/public/app/transcript-separators.mjs +48 -11
- package/server/public/app/viewer-chat-gallery.mjs +59 -0
- package/server/public/app/viewer-controls-state.mjs +59 -0
- package/server/public/app/viewer-gallery.mjs +46 -0
- package/server/public/app/viewer-gestures.mjs +105 -0
- package/server/public/app/viewer-history.mjs +43 -0
- package/server/public/app/viewer-tap.mjs +112 -0
- package/server/public/app-icon-192.png +0 -0
- package/server/public/app-icon-512.png +0 -0
- package/server/public/app-icon-light-192.png +0 -0
- package/server/public/app-icon-light-512.png +0 -0
- package/server/public/app-icon-light-maskable-512.png +0 -0
- package/server/public/app-icon-light.svg +4 -0
- package/server/public/app-icon-maskable-512.png +0 -0
- package/server/public/app-icon.svg +3 -3
- package/server/public/favicon.ico +0 -0
- package/server/public/index.html +487 -67
- package/server/public/manifest.webmanifest +9 -3
- package/server/public/sw.js +6 -1
- package/server/repositories/message-pin-statements.mjs +39 -0
- package/server/repositories/message-repository.mjs +5 -0
- package/server/routes/messages-routes.mjs +36 -62
- package/server/routes/sessions-routes.mjs +107 -26
- package/server/server-runtime.mjs +39 -8
- package/server/services/claude-context-usage.mjs +51 -3
- package/server/services/cloudflared-tunnel-service.mjs +139 -41
- package/server/services/file-serving.mjs +155 -0
- package/server/services/message-pin-list.mjs +52 -0
- package/server/services/oar-cli-helpers.mjs +205 -4
- package/server/services/oar-cli-menu.mjs +240 -0
- package/server/services/oar-cli-wsl.mjs +129 -0
- package/server/services/pwa-manifest-theme.mjs +30 -0
- package/server/services/safe-served-content.mjs +61 -11
- package/server/services/session-history-refresh-service.mjs +24 -0
- package/server/services/session-worker-registry-service.mjs +18 -1
- package/server/services/update-install-service.mjs +9 -3
package/server/public/index.html
CHANGED
|
@@ -3,12 +3,23 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, viewport-fit=cover, interactive-widget=resizes-content">
|
|
6
|
-
<meta name="theme-color" content="#
|
|
6
|
+
<meta name="theme-color" content="#0d1117">
|
|
7
7
|
<meta name="application-name" content="OAR">
|
|
8
8
|
<meta name="apple-mobile-web-app-title" content="OAR">
|
|
9
9
|
<meta name="mobile-web-app-capable" content="yes">
|
|
10
10
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
11
11
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
12
|
+
<script>
|
|
13
|
+
// The stored Theme setting, applied before the first paint so the start
|
|
14
|
+
// screen (and its logo) already match it.
|
|
15
|
+
const __STORED_THEME = (() => {
|
|
16
|
+
try { return window.localStorage.getItem('copilot_theme') === 'light' ? 'light' : 'dark'; } catch { return 'dark'; }
|
|
17
|
+
})();
|
|
18
|
+
if (__STORED_THEME === 'light') {
|
|
19
|
+
document.documentElement.setAttribute('data-theme', 'light');
|
|
20
|
+
document.querySelector('meta[name="theme-color"]')?.setAttribute('content', '#ffffff');
|
|
21
|
+
}
|
|
22
|
+
</script>
|
|
12
23
|
<script>
|
|
13
24
|
window.__COPILOT_APP_CONFIG = { basePath: '' };
|
|
14
25
|
const __APP_CONFIG = window.__COPILOT_APP_CONFIG;
|
|
@@ -163,13 +174,35 @@
|
|
|
163
174
|
if (errorEl) errorEl.textContent = 'Could not reach the relay';
|
|
164
175
|
}
|
|
165
176
|
};
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
177
|
+
// Manifest and tab icons for one theme: the dark icon set, or its
|
|
178
|
+
// `app-icon-light*` twin with a white launch background.
|
|
179
|
+
const __themeAssetLinks = (theme) => {
|
|
180
|
+
const light = theme === 'light';
|
|
181
|
+
const icon = light ? 'app-icon-light' : 'app-icon';
|
|
182
|
+
return [
|
|
183
|
+
...(__IS_SHARED_ROUTE ? [] : [
|
|
184
|
+
{ rel: 'manifest', href: `${__APP_BASE}/manifest.webmanifest?${__PWA_BUST}${light ? '&theme=light' : ''}` },
|
|
185
|
+
]),
|
|
186
|
+
{ rel: 'icon', type: 'image/png', sizes: '192x192', href: `${__APP_BASE}/${icon}-192.png?${__PWA_BUST}` },
|
|
187
|
+
{ rel: 'icon', type: 'image/png', sizes: '512x512', href: `${__APP_BASE}/${icon}-512.png?${__PWA_BUST}` },
|
|
188
|
+
{ rel: 'icon', type: 'image/svg+xml', href: `${__APP_BASE}/${icon}.svg?${__PWA_BUST}` },
|
|
189
|
+
{ rel: 'apple-touch-icon', href: `${__APP_BASE}/${icon}-192.png?${__PWA_BUST}` },
|
|
190
|
+
];
|
|
191
|
+
};
|
|
192
|
+
const __themeAssetLinkHtml = (theme) => __themeAssetLinks(theme)
|
|
193
|
+
.map((link) => `<link data-theme-asset="1" ${Object.entries(link).map(([key, value]) => `${key}="${value}"`).join(' ')}>`)
|
|
194
|
+
.join('');
|
|
195
|
+
document.write(__themeAssetLinkHtml(__STORED_THEME));
|
|
196
|
+
document.write(`<style>
|
|
197
|
+
.brand-logo { background-image: url("${__APP_BASE}/app-icon.svg?${__PWA_BUST}"); }
|
|
198
|
+
:root[data-theme="light"] .brand-logo { background-image: url("${__APP_BASE}/app-icon-light.svg?${__PWA_BUST}"); }
|
|
199
|
+
</style>`);
|
|
200
|
+
// A Theme change swaps them in place (settings-modal's updateTheme).
|
|
201
|
+
window.__applyThemeAssets = (theme) => {
|
|
202
|
+
const next = theme === 'light' ? 'light' : 'dark';
|
|
203
|
+
for (const node of Array.from(document.head.querySelectorAll('link[data-theme-asset]'))) node.remove();
|
|
204
|
+
document.head.insertAdjacentHTML('beforeend', __themeAssetLinkHtml(next));
|
|
205
|
+
};
|
|
173
206
|
</script>
|
|
174
207
|
<title>OAR — Open Agent Relay</title>
|
|
175
208
|
<script>document.write('<scr'+'ipt src="'+__APP_BASE+'/socket.io/socket.io.js"><\/scr'+'ipt>');</script>
|
|
@@ -384,12 +417,44 @@
|
|
|
384
417
|
display: none; align-items: center; justify-content: center;
|
|
385
418
|
height: var(--app-height); padding: 24px;
|
|
386
419
|
}
|
|
420
|
+
/* The start screen lies over the page until the app (or the sign-in box)
|
|
421
|
+
is ready. In the installed app it takes over from the system's launch
|
|
422
|
+
screen: the logo starts where and as large as the launch screen showed
|
|
423
|
+
it, then settles into this layout while the label fades in. */
|
|
387
424
|
#startup-loading {
|
|
388
|
-
|
|
389
|
-
|
|
425
|
+
position: fixed; inset: 0; z-index: 10000;
|
|
426
|
+
display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px;
|
|
427
|
+
padding: 24px 24px 124px;
|
|
428
|
+
background: var(--bg);
|
|
390
429
|
color: var(--muted);
|
|
391
430
|
font-size: var(--fs-core-subtitle);
|
|
431
|
+
transition: opacity 150ms ease;
|
|
432
|
+
}
|
|
433
|
+
#startup-loading.is-leaving { opacity: 0; }
|
|
434
|
+
#startup-loading .brand-logo { width: 148px; height: 148px; }
|
|
435
|
+
#startup-loading.is-animated .brand-logo {
|
|
436
|
+
animation: startup-logo-settle 500ms cubic-bezier(0.22, 1, 0.36, 1) 200ms both;
|
|
437
|
+
}
|
|
438
|
+
#startup-loading.is-animated .startup-label {
|
|
439
|
+
animation: startup-label-in 400ms ease 450ms both;
|
|
440
|
+
}
|
|
441
|
+
#startup-loading .startup-label { transition: opacity 150ms ease; }
|
|
442
|
+
@keyframes startup-logo-settle {
|
|
443
|
+
from { transform: translate(var(--startup-dx, 0px), var(--startup-dy, 0px)) scale(var(--startup-scale, 1)); }
|
|
444
|
+
to { transform: none; }
|
|
392
445
|
}
|
|
446
|
+
@keyframes startup-label-in {
|
|
447
|
+
from { opacity: 0; }
|
|
448
|
+
to { opacity: 1; }
|
|
449
|
+
}
|
|
450
|
+
/* The logo for the active theme. A background, not an <img>: the browser
|
|
451
|
+
fetches only the one the matching rule names (the rules with the URLs are
|
|
452
|
+
written by the head script, which knows the relay's base path). */
|
|
453
|
+
.brand-logo {
|
|
454
|
+
width: 128px; height: 128px; flex: none;
|
|
455
|
+
background-position: center; background-size: contain; background-repeat: no-repeat;
|
|
456
|
+
}
|
|
457
|
+
.auth-box .brand-logo { width: 96px; height: 96px; margin: 0 auto 16px; }
|
|
393
458
|
.auth-box {
|
|
394
459
|
background: var(--bg2); border: 1px solid var(--border); border-radius: 12px;
|
|
395
460
|
padding: 40px 32px; width: 100%; max-width: 400px; text-align: center;
|
|
@@ -507,6 +572,21 @@
|
|
|
507
572
|
opacity: 0.5;
|
|
508
573
|
cursor: not-allowed;
|
|
509
574
|
}
|
|
575
|
+
/* An icon with a number beside it (📍 3) grows with the number. */
|
|
576
|
+
.header-icon-btn.has-count {
|
|
577
|
+
width: auto;
|
|
578
|
+
min-width: 32px;
|
|
579
|
+
padding: 0 7px;
|
|
580
|
+
gap: 4px;
|
|
581
|
+
}
|
|
582
|
+
.header-icon-count {
|
|
583
|
+
font-size: var(--fs-meta-sm);
|
|
584
|
+
font-variant-numeric: tabular-nums;
|
|
585
|
+
}
|
|
586
|
+
.header-icon-count:empty { display: none; }
|
|
587
|
+
/* The display rule above outranks the UA [hidden] rule, same trap as the
|
|
588
|
+
other [hidden] overrides in this file. */
|
|
589
|
+
#pinned-messages-btn[hidden] { display: none; }
|
|
510
590
|
#cli-dot {
|
|
511
591
|
width: 12px;
|
|
512
592
|
height: 12px;
|
|
@@ -1352,6 +1432,9 @@
|
|
|
1352
1432
|
padding: 2px 10px;
|
|
1353
1433
|
background: var(--bg2);
|
|
1354
1434
|
}
|
|
1435
|
+
/* A compaction that is still running: the label trails the bouncing dots. */
|
|
1436
|
+
.transcript-separator.is-pending .dots { margin-left: 6px; }
|
|
1437
|
+
.transcript-separator.is-pending .dots span { width: 4px; height: 4px; }
|
|
1355
1438
|
|
|
1356
1439
|
.empty-state {
|
|
1357
1440
|
flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
@@ -1703,8 +1786,13 @@
|
|
|
1703
1786
|
opacity: 0;
|
|
1704
1787
|
pointer-events: none;
|
|
1705
1788
|
}
|
|
1789
|
+
/* `.msg-actions-revealed`: a tap on the bubble on a touch screen. The tap
|
|
1790
|
+
also leaves an emulated :hover behind, but Chromium drops that hover at
|
|
1791
|
+
the next scroll or layout change (seen on Linux), which left the row
|
|
1792
|
+
untappable; the class holds the reveal until a tap lands elsewhere. */
|
|
1706
1793
|
.msg-bubble:hover .msg-share-visibility-btn[data-hidden-from-shares="false"],
|
|
1707
|
-
.msg-bubble:focus-within .msg-share-visibility-btn[data-hidden-from-shares="false"]
|
|
1794
|
+
.msg-bubble:focus-within .msg-share-visibility-btn[data-hidden-from-shares="false"],
|
|
1795
|
+
.msg-bubble.msg-actions-revealed .msg-share-visibility-btn[data-hidden-from-shares="false"] {
|
|
1708
1796
|
opacity: 1;
|
|
1709
1797
|
pointer-events: auto;
|
|
1710
1798
|
}
|
|
@@ -1712,7 +1800,46 @@
|
|
|
1712
1800
|
.msg.user .msg-share-visibility-btn { color: var(--user-bubble-fg); opacity: 0.8; }
|
|
1713
1801
|
.msg.user .msg-share-visibility-btn[data-hidden-from-shares="false"] { opacity: 0; }
|
|
1714
1802
|
.msg.user .msg-bubble:hover .msg-share-visibility-btn[data-hidden-from-shares="false"],
|
|
1715
|
-
.msg.user .msg-bubble:focus-within .msg-share-visibility-btn[data-hidden-from-shares="false"]
|
|
1803
|
+
.msg.user .msg-bubble:focus-within .msg-share-visibility-btn[data-hidden-from-shares="false"],
|
|
1804
|
+
.msg.user .msg-bubble.msg-actions-revealed .msg-share-visibility-btn[data-hidden-from-shares="false"] { opacity: 0.8; }
|
|
1805
|
+
/* The pin half of the same corner row: `Pin` is revealed like `Hide`, a
|
|
1806
|
+
pinned message keeps its label and `Unpin` in view. */
|
|
1807
|
+
.msg-share-visibility { flex-wrap: wrap; }
|
|
1808
|
+
.msg-pinned-label {
|
|
1809
|
+
color: var(--muted);
|
|
1810
|
+
font-size: var(--fs-ui-chip);
|
|
1811
|
+
white-space: nowrap;
|
|
1812
|
+
}
|
|
1813
|
+
.msg-pin-btn {
|
|
1814
|
+
appearance: none;
|
|
1815
|
+
border: 0;
|
|
1816
|
+
background: transparent;
|
|
1817
|
+
color: var(--muted);
|
|
1818
|
+
cursor: pointer;
|
|
1819
|
+
font-size: var(--fs-ui-chip);
|
|
1820
|
+
line-height: 1;
|
|
1821
|
+
padding: 4px;
|
|
1822
|
+
text-decoration: underline;
|
|
1823
|
+
text-underline-offset: 2px;
|
|
1824
|
+
}
|
|
1825
|
+
.msg-pin-btn:hover:not(:disabled) { color: var(--accent); }
|
|
1826
|
+
.msg-pin-btn:disabled { cursor: wait; opacity: 0.55; }
|
|
1827
|
+
.msg-pin-btn[data-pinned="false"] {
|
|
1828
|
+
opacity: 0;
|
|
1829
|
+
pointer-events: none;
|
|
1830
|
+
}
|
|
1831
|
+
.msg-bubble:hover .msg-pin-btn[data-pinned="false"],
|
|
1832
|
+
.msg-bubble:focus-within .msg-pin-btn[data-pinned="false"],
|
|
1833
|
+
.msg-bubble.msg-actions-revealed .msg-pin-btn[data-pinned="false"] {
|
|
1834
|
+
opacity: 1;
|
|
1835
|
+
pointer-events: auto;
|
|
1836
|
+
}
|
|
1837
|
+
.msg.user .msg-pinned-label,
|
|
1838
|
+
.msg.user .msg-pin-btn { color: var(--user-bubble-fg); opacity: 0.8; }
|
|
1839
|
+
.msg.user .msg-pin-btn[data-pinned="false"] { opacity: 0; }
|
|
1840
|
+
.msg.user .msg-bubble:hover .msg-pin-btn[data-pinned="false"],
|
|
1841
|
+
.msg.user .msg-bubble:focus-within .msg-pin-btn[data-pinned="false"],
|
|
1842
|
+
.msg.user .msg-bubble.msg-actions-revealed .msg-pin-btn[data-pinned="false"] { opacity: 0.8; }
|
|
1716
1843
|
.thinking-thoughts-panel {
|
|
1717
1844
|
margin-bottom: 8px;
|
|
1718
1845
|
min-width: 0;
|
|
@@ -1744,6 +1871,12 @@
|
|
|
1744
1871
|
word-break: break-word;
|
|
1745
1872
|
}
|
|
1746
1873
|
.thinking-stream[hidden] { display: none; }
|
|
1874
|
+
.thinking-compacting-note {
|
|
1875
|
+
color: var(--muted);
|
|
1876
|
+
font-size: var(--fs-meta-sm);
|
|
1877
|
+
font-style: italic;
|
|
1878
|
+
}
|
|
1879
|
+
.thinking-compacting-note[hidden] { display: none; }
|
|
1747
1880
|
.thinking-stream > :first-child { margin-top: 0; }
|
|
1748
1881
|
.thinking-stream > :last-child { margin-bottom: 0; }
|
|
1749
1882
|
.thinking-activity {
|
|
@@ -2097,6 +2230,9 @@
|
|
|
2097
2230
|
font-size: 0.76rem;
|
|
2098
2231
|
color: var(--muted);
|
|
2099
2232
|
}
|
|
2233
|
+
.ctx-autocompact-notes { display: flex; flex-direction: column; gap: 4px; }
|
|
2234
|
+
.ctx-autocompact-notes:empty { display: none; }
|
|
2235
|
+
.ctx-autocompact-warning { color: var(--warn); }
|
|
2100
2236
|
/* Claude-only thinking control (on/off + text visibility), under the
|
|
2101
2237
|
auto-compact slider. */
|
|
2102
2238
|
/* The folder's commit attribution (attribution-control.mjs), styled like
|
|
@@ -3149,28 +3285,62 @@
|
|
|
3149
3285
|
.workspace-file-link { text-decoration: underline; }
|
|
3150
3286
|
.workspace-file-link:hover { opacity: 0.9; }
|
|
3151
3287
|
|
|
3288
|
+
/* The file viewer fills the screen. Its chrome (title, toolbar, meta line
|
|
3289
|
+
above; position and prev/next below) floats over the content and slides
|
|
3290
|
+
away when the controls are hidden. Text kinds keep the app background and
|
|
3291
|
+
a readable column; media kinds go black edge to edge. */
|
|
3152
3292
|
#file-preview-modal {
|
|
3153
3293
|
position: fixed; inset: 0; z-index: 11000;
|
|
3154
|
-
display: none;
|
|
3155
|
-
background:
|
|
3156
|
-
|
|
3294
|
+
display: none;
|
|
3295
|
+
background: var(--bg2);
|
|
3296
|
+
--file-viewer-chrome-top: 0px;
|
|
3157
3297
|
}
|
|
3158
|
-
#file-preview-modal.visible { display:
|
|
3298
|
+
#file-preview-modal.visible { display: block; }
|
|
3299
|
+
#file-preview-modal.media-mode { background: #000; }
|
|
3159
3300
|
.file-preview-dialog {
|
|
3160
|
-
|
|
3161
|
-
max-height: min(90vh, 900px);
|
|
3162
|
-
display: flex;
|
|
3163
|
-
flex-direction: column;
|
|
3164
|
-
background: var(--bg2);
|
|
3165
|
-
border: 1px solid var(--border);
|
|
3166
|
-
border-radius: 14px;
|
|
3301
|
+
position: absolute; inset: 0;
|
|
3167
3302
|
overflow: hidden;
|
|
3168
3303
|
}
|
|
3304
|
+
.file-preview-chrome {
|
|
3305
|
+
position: absolute; left: 0; right: 0; z-index: 2;
|
|
3306
|
+
transition: opacity 0.18s ease, transform 0.18s ease;
|
|
3307
|
+
}
|
|
3308
|
+
.file-preview-chrome-top {
|
|
3309
|
+
top: 0;
|
|
3310
|
+
padding-top: env(safe-area-inset-top);
|
|
3311
|
+
background: color-mix(in srgb, var(--bg3) 92%, transparent);
|
|
3312
|
+
-webkit-backdrop-filter: blur(10px);
|
|
3313
|
+
backdrop-filter: blur(10px);
|
|
3314
|
+
border-bottom: 1px solid var(--border);
|
|
3315
|
+
}
|
|
3316
|
+
#file-preview-modal.media-mode .file-preview-chrome-top {
|
|
3317
|
+
background: linear-gradient(rgba(0, 0, 0, 0.82), rgba(0, 0, 0, 0.62) 70%, rgba(0, 0, 0, 0));
|
|
3318
|
+
border-bottom: 0;
|
|
3319
|
+
-webkit-backdrop-filter: none;
|
|
3320
|
+
backdrop-filter: none;
|
|
3321
|
+
color: #fff;
|
|
3322
|
+
padding-bottom: 18px;
|
|
3323
|
+
}
|
|
3324
|
+
.file-preview-chrome-bottom {
|
|
3325
|
+
bottom: 0;
|
|
3326
|
+
display: flex; align-items: center; justify-content: center; gap: 14px;
|
|
3327
|
+
padding: 12px 14px calc(12px + env(safe-area-inset-bottom));
|
|
3328
|
+
background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.62));
|
|
3329
|
+
color: #fff;
|
|
3330
|
+
}
|
|
3331
|
+
.file-preview-chrome-bottom[hidden] { display: none; }
|
|
3332
|
+
#file-preview-modal.controls-hidden .file-preview-chrome { opacity: 0; pointer-events: none; }
|
|
3333
|
+
#file-preview-modal.controls-hidden .file-preview-chrome-top { transform: translateY(-12px); }
|
|
3334
|
+
#file-preview-modal.controls-hidden .file-preview-chrome-bottom { transform: translateY(12px); }
|
|
3335
|
+
.file-preview-position {
|
|
3336
|
+
min-width: 4ch; text-align: center;
|
|
3337
|
+
font-size: var(--fs-meta-md);
|
|
3338
|
+
font-variant-numeric: tabular-nums;
|
|
3339
|
+
}
|
|
3340
|
+
.file-preview-nav { font-size: 1.3rem; line-height: 1; padding: 4px 14px; }
|
|
3169
3341
|
.file-preview-header {
|
|
3170
3342
|
display: flex; align-items: center; justify-content: space-between; gap: 10px;
|
|
3171
3343
|
padding: 12px 14px;
|
|
3172
|
-
border-bottom: 1px solid var(--border);
|
|
3173
|
-
background: var(--bg3);
|
|
3174
3344
|
}
|
|
3175
3345
|
.file-preview-title {
|
|
3176
3346
|
min-width: 0;
|
|
@@ -3195,23 +3365,35 @@
|
|
|
3195
3365
|
}
|
|
3196
3366
|
.file-preview-toolbar {
|
|
3197
3367
|
display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
|
|
3198
|
-
padding:
|
|
3199
|
-
border-bottom: 1px solid var(--border);
|
|
3368
|
+
padding: 0 14px 10px;
|
|
3200
3369
|
}
|
|
3201
3370
|
.file-preview-btn {
|
|
3202
3371
|
border: 1px solid var(--border);
|
|
3203
|
-
background: var(--
|
|
3372
|
+
background: var(--bg2);
|
|
3204
3373
|
color: var(--text);
|
|
3205
3374
|
border-radius: 8px;
|
|
3206
3375
|
padding: 6px 10px;
|
|
3207
3376
|
font-size: var(--fs-meta-md);
|
|
3208
3377
|
cursor: pointer;
|
|
3378
|
+
text-decoration: none;
|
|
3209
3379
|
}
|
|
3210
3380
|
.file-preview-btn.active {
|
|
3211
3381
|
border-color: var(--accent);
|
|
3212
3382
|
color: var(--accent);
|
|
3213
3383
|
}
|
|
3214
3384
|
.file-preview-btn[hidden] { display: none; }
|
|
3385
|
+
#file-preview-modal.media-mode .file-preview-btn,
|
|
3386
|
+
#file-preview-modal.media-mode .file-preview-close {
|
|
3387
|
+
background: rgba(255, 255, 255, 0.14);
|
|
3388
|
+
border-color: rgba(255, 255, 255, 0.22);
|
|
3389
|
+
color: #fff;
|
|
3390
|
+
}
|
|
3391
|
+
#file-preview-modal.media-mode .file-preview-btn.active {
|
|
3392
|
+
border-color: var(--accent);
|
|
3393
|
+
color: #fff;
|
|
3394
|
+
background: color-mix(in srgb, var(--accent) 45%, transparent);
|
|
3395
|
+
}
|
|
3396
|
+
#file-preview-modal.media-mode .file-preview-meta { color: rgba(255, 255, 255, 0.72); }
|
|
3215
3397
|
.file-preview-warning {
|
|
3216
3398
|
display: none;
|
|
3217
3399
|
margin-left: auto;
|
|
@@ -3220,16 +3402,30 @@
|
|
|
3220
3402
|
}
|
|
3221
3403
|
.file-preview-warning.visible { display: inline; }
|
|
3222
3404
|
.file-preview-meta {
|
|
3223
|
-
padding:
|
|
3405
|
+
padding: 0 14px 8px;
|
|
3224
3406
|
font-size: var(--fs-meta-sm);
|
|
3225
3407
|
color: var(--muted);
|
|
3226
|
-
border-bottom: 1px dashed var(--border);
|
|
3227
3408
|
}
|
|
3409
|
+
.file-preview-meta:empty { display: none; }
|
|
3410
|
+
/* Text starts below the chrome so the first line is readable with the
|
|
3411
|
+
controls up; the chrome's height is measured into the variable. */
|
|
3228
3412
|
.file-preview-body {
|
|
3229
|
-
|
|
3230
|
-
min-height: 0; /* prevents flex child from overflowing the dialog's max-height */
|
|
3413
|
+
position: absolute; inset: 0;
|
|
3231
3414
|
overflow: auto;
|
|
3232
|
-
|
|
3415
|
+
-webkit-overflow-scrolling: touch;
|
|
3416
|
+
padding: calc(var(--file-viewer-chrome-top) + 12px) 14px calc(28px + env(safe-area-inset-bottom));
|
|
3417
|
+
/* Up and down scroll the text; sideways is left to the viewer (a swipe
|
|
3418
|
+
to the next file). A block that scrolls sideways itself keeps that. */
|
|
3419
|
+
touch-action: pan-y pinch-zoom;
|
|
3420
|
+
}
|
|
3421
|
+
.file-preview-markdown pre,
|
|
3422
|
+
.file-preview-code pre,
|
|
3423
|
+
.file-preview-markdown table { touch-action: pan-x pan-y; }
|
|
3424
|
+
.file-preview-nav:disabled { opacity: 0.35; cursor: default; }
|
|
3425
|
+
.file-preview-markdown,
|
|
3426
|
+
.file-preview-body > .file-preview-code {
|
|
3427
|
+
max-width: 56rem;
|
|
3428
|
+
margin: 0 auto;
|
|
3233
3429
|
}
|
|
3234
3430
|
.file-preview-markdown {
|
|
3235
3431
|
line-height: 1.5;
|
|
@@ -3292,42 +3488,71 @@
|
|
|
3292
3488
|
user-select: none;
|
|
3293
3489
|
-webkit-user-drag: none;
|
|
3294
3490
|
}
|
|
3295
|
-
.file-preview-video-shell
|
|
3491
|
+
.file-preview-video-shell,
|
|
3492
|
+
.file-preview-audio-shell {
|
|
3296
3493
|
display: flex;
|
|
3297
3494
|
flex-direction: column;
|
|
3495
|
+
align-items: center;
|
|
3496
|
+
justify-content: center;
|
|
3298
3497
|
gap: 10px;
|
|
3299
3498
|
width: 100%;
|
|
3300
3499
|
height: 100%;
|
|
3301
3500
|
}
|
|
3302
3501
|
.file-preview-video {
|
|
3303
|
-
width: 100%;
|
|
3304
3502
|
max-width: 100%;
|
|
3305
|
-
max-height:
|
|
3503
|
+
max-height: 100%;
|
|
3504
|
+
width: auto;
|
|
3505
|
+
height: auto;
|
|
3306
3506
|
background: #000;
|
|
3307
|
-
border-radius: 10px;
|
|
3308
3507
|
}
|
|
3309
|
-
/*
|
|
3508
|
+
/* The player takes the card's width; a percentage on the player alone
|
|
3509
|
+
would resolve against a content-sized flex row and come out as zero. */
|
|
3510
|
+
.file-preview-audio-shell .file-preview-card { width: min(560px, calc(100% - 28px)); }
|
|
3511
|
+
.file-preview-audio-shell .file-preview-card-actions { width: 100%; }
|
|
3512
|
+
.file-preview-audio-shell audio { width: 100%; }
|
|
3513
|
+
/* Media and cards: no scrolling, the content is centred on the whole screen. */
|
|
3310
3514
|
.file-preview-body.image-zoom-mode,
|
|
3311
|
-
.file-preview-body.video-preview-mode
|
|
3312
|
-
|
|
3313
|
-
|
|
3515
|
+
.file-preview-body.video-preview-mode,
|
|
3516
|
+
.file-preview-body.audio-preview-mode,
|
|
3517
|
+
.file-preview-body.card-preview-mode {
|
|
3314
3518
|
overflow: hidden;
|
|
3315
3519
|
padding: 0;
|
|
3316
3520
|
display: flex;
|
|
3317
3521
|
flex-direction: column;
|
|
3318
|
-
|
|
3522
|
+
touch-action: none;
|
|
3319
3523
|
}
|
|
3320
3524
|
.file-preview-body.image-zoom-mode .file-preview-image {
|
|
3321
3525
|
flex: 1;
|
|
3322
|
-
min-height: 0; /*
|
|
3323
|
-
|
|
3526
|
+
min-height: 0; /* lets the img's max-height:100% resolve */
|
|
3527
|
+
}
|
|
3528
|
+
.file-preview-body.image-zoom-mode .file-preview-image img { border-radius: 0; }
|
|
3529
|
+
/* A file with nothing to show inline (a PDF, an unknown binary) or an audio
|
|
3530
|
+
file: a card in the middle with the name, the size and what can be done. */
|
|
3531
|
+
.file-preview-card {
|
|
3532
|
+
margin: auto;
|
|
3533
|
+
max-width: min(420px, calc(100% - 28px));
|
|
3534
|
+
display: flex; flex-direction: column; align-items: center; gap: 8px;
|
|
3535
|
+
text-align: center;
|
|
3536
|
+
padding: 24px;
|
|
3537
|
+
border: 1px solid var(--border);
|
|
3538
|
+
border-radius: 16px;
|
|
3539
|
+
background: var(--bg3);
|
|
3324
3540
|
}
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3541
|
+
#file-preview-modal.media-mode .file-preview-card {
|
|
3542
|
+
background: rgba(255, 255, 255, 0.08);
|
|
3543
|
+
border-color: rgba(255, 255, 255, 0.16);
|
|
3544
|
+
color: #fff;
|
|
3545
|
+
}
|
|
3546
|
+
.file-preview-card-icon { font-size: 3rem; line-height: 1; }
|
|
3547
|
+
.file-preview-card-name {
|
|
3548
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
3549
|
+
font-size: var(--fs-meta-md);
|
|
3550
|
+
word-break: break-all;
|
|
3330
3551
|
}
|
|
3552
|
+
.file-preview-card-meta { font-size: var(--fs-meta-sm); color: var(--muted); }
|
|
3553
|
+
#file-preview-modal.media-mode .file-preview-card-meta { color: rgba(255, 255, 255, 0.72); }
|
|
3554
|
+
.file-preview-card-actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-top: 8px; }
|
|
3555
|
+
.file-preview-card-actions .file-preview-btn { padding: 10px 16px; }
|
|
3331
3556
|
.msg-attachment-video-chip {
|
|
3332
3557
|
display: inline-flex;
|
|
3333
3558
|
align-items: center;
|
|
@@ -3344,6 +3569,11 @@
|
|
|
3344
3569
|
color: var(--muted);
|
|
3345
3570
|
margin-top: 10px;
|
|
3346
3571
|
}
|
|
3572
|
+
.file-preview-body.image-zoom-mode .file-preview-note,
|
|
3573
|
+
.file-preview-body.video-preview-mode .file-preview-note {
|
|
3574
|
+
margin: auto;
|
|
3575
|
+
padding: 14px;
|
|
3576
|
+
}
|
|
3347
3577
|
|
|
3348
3578
|
/* Annotation editor. Clones the file-preview shell, but sits above it
|
|
3349
3579
|
(z-index 11500) so annotating a sent image can layer over the lightbox. */
|
|
@@ -4147,6 +4377,84 @@
|
|
|
4147
4377
|
font-size: var(--fs-meta-sm);
|
|
4148
4378
|
padding: 8px 6px;
|
|
4149
4379
|
}
|
|
4380
|
+
/* Pinned messages list (the summary modal, kind 'pinned-messages'). */
|
|
4381
|
+
.pinned-list {
|
|
4382
|
+
display: flex;
|
|
4383
|
+
flex-direction: column;
|
|
4384
|
+
gap: 8px;
|
|
4385
|
+
}
|
|
4386
|
+
.pinned-row {
|
|
4387
|
+
display: flex;
|
|
4388
|
+
align-items: stretch;
|
|
4389
|
+
border: 1px solid var(--border);
|
|
4390
|
+
background: var(--bg3);
|
|
4391
|
+
border-radius: 10px;
|
|
4392
|
+
overflow: hidden;
|
|
4393
|
+
}
|
|
4394
|
+
.pinned-row:hover,
|
|
4395
|
+
.pinned-row:focus-within {
|
|
4396
|
+
border-color: var(--accent);
|
|
4397
|
+
}
|
|
4398
|
+
.pinned-row-jump {
|
|
4399
|
+
appearance: none;
|
|
4400
|
+
border: 0;
|
|
4401
|
+
background: transparent;
|
|
4402
|
+
color: var(--text);
|
|
4403
|
+
font: inherit;
|
|
4404
|
+
text-align: left;
|
|
4405
|
+
cursor: pointer;
|
|
4406
|
+
flex: 1;
|
|
4407
|
+
min-width: 0;
|
|
4408
|
+
padding: 10px 12px;
|
|
4409
|
+
display: flex;
|
|
4410
|
+
flex-direction: column;
|
|
4411
|
+
gap: 4px;
|
|
4412
|
+
}
|
|
4413
|
+
.pinned-row-meta {
|
|
4414
|
+
font-size: var(--fs-meta-xs);
|
|
4415
|
+
color: var(--muted);
|
|
4416
|
+
}
|
|
4417
|
+
.pinned-row-chip {
|
|
4418
|
+
border: 1px solid var(--border);
|
|
4419
|
+
border-radius: 999px;
|
|
4420
|
+
padding: 0 6px;
|
|
4421
|
+
white-space: nowrap;
|
|
4422
|
+
}
|
|
4423
|
+
.pinned-row-preview {
|
|
4424
|
+
font-size: var(--fs-meta-sm);
|
|
4425
|
+
line-height: 1.4;
|
|
4426
|
+
display: -webkit-box;
|
|
4427
|
+
-webkit-line-clamp: 2;
|
|
4428
|
+
-webkit-box-orient: vertical;
|
|
4429
|
+
overflow: hidden;
|
|
4430
|
+
overflow-wrap: anywhere;
|
|
4431
|
+
}
|
|
4432
|
+
.pinned-row-no-text {
|
|
4433
|
+
color: var(--muted);
|
|
4434
|
+
font-style: italic;
|
|
4435
|
+
}
|
|
4436
|
+
/* 44px: a thumb-sized target, always in view (no hover on a phone). */
|
|
4437
|
+
.pinned-row-unpin {
|
|
4438
|
+
appearance: none;
|
|
4439
|
+
border: 0;
|
|
4440
|
+
border-left: 1px solid var(--border);
|
|
4441
|
+
background: transparent;
|
|
4442
|
+
color: var(--muted);
|
|
4443
|
+
cursor: pointer;
|
|
4444
|
+
flex: 0 0 44px;
|
|
4445
|
+
min-height: 44px;
|
|
4446
|
+
font-size: var(--fs-ui-icon);
|
|
4447
|
+
line-height: 1;
|
|
4448
|
+
}
|
|
4449
|
+
.pinned-row-unpin:hover {
|
|
4450
|
+
color: var(--danger);
|
|
4451
|
+
background: rgba(248, 81, 73, 0.08);
|
|
4452
|
+
}
|
|
4453
|
+
.pinned-empty {
|
|
4454
|
+
color: var(--muted);
|
|
4455
|
+
font-size: var(--fs-meta-sm);
|
|
4456
|
+
padding: 8px 6px;
|
|
4457
|
+
}
|
|
4150
4458
|
|
|
4151
4459
|
#git-changes-modal,
|
|
4152
4460
|
#git-diff-modal {
|
|
@@ -5227,6 +5535,7 @@
|
|
|
5227
5535
|
}
|
|
5228
5536
|
#pending-question-banner { font-size: var(--fs-ui-chip); padding: 3px 8px; }
|
|
5229
5537
|
.header-icon-btn { width: 30px; height: 30px; border-radius: 8px; font-size: var(--fs-ui-icon); }
|
|
5538
|
+
.header-icon-btn.has-count { min-width: 30px; padding: 0 6px; }
|
|
5230
5539
|
#chat-actions-menu { min-width: 200px; }
|
|
5231
5540
|
/* Slightly taller than desktop so the tap target barely changes; the row
|
|
5232
5541
|
pitch still drops from 36px to 32px thanks to the tighter menu gap. */
|
|
@@ -5464,6 +5773,15 @@
|
|
|
5464
5773
|
flex: 1 1 calc(50% - 4px);
|
|
5465
5774
|
min-height: 40px;
|
|
5466
5775
|
}
|
|
5776
|
+
/* The pin list has one action, Close: the full-width action row above
|
|
5777
|
+
would leave its title no room. Title left, close button right. */
|
|
5778
|
+
#summary-modal[data-kind="pinned-messages"] .summary-header-actions {
|
|
5779
|
+
width: auto;
|
|
5780
|
+
}
|
|
5781
|
+
#summary-modal[data-kind="pinned-messages"] .summary-header-actions .summary-close {
|
|
5782
|
+
flex: 0 0 auto;
|
|
5783
|
+
min-width: 40px;
|
|
5784
|
+
}
|
|
5467
5785
|
.model-variant-row {
|
|
5468
5786
|
min-height: 48px;
|
|
5469
5787
|
padding: 10px 4px;
|
|
@@ -5585,6 +5903,18 @@
|
|
|
5585
5903
|
::-webkit-scrollbar { width: 6px; }
|
|
5586
5904
|
::-webkit-scrollbar-track { background: transparent; }
|
|
5587
5905
|
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
|
|
5906
|
+
/* With a mouse the track is twice as wide and the thumb is drawn thin in
|
|
5907
|
+
its middle; the thumb fills the track while the pointer is on the
|
|
5908
|
+
scrollbar and for a moment after (scrollbar-hover.mjs). The track keeps
|
|
5909
|
+
its width, so the page does not move when the thumb grows. */
|
|
5910
|
+
@media (hover: hover) and (pointer: fine) {
|
|
5911
|
+
::-webkit-scrollbar { width: 12px; }
|
|
5912
|
+
::-webkit-scrollbar-thumb {
|
|
5913
|
+
border: 3px solid transparent; border-top-width: 0; border-bottom-width: 0;
|
|
5914
|
+
background-clip: padding-box; border-radius: 6px;
|
|
5915
|
+
}
|
|
5916
|
+
.scrollbar-wide::-webkit-scrollbar-thumb { border-width: 0; background: var(--muted); }
|
|
5917
|
+
}
|
|
5588
5918
|
</style>
|
|
5589
5919
|
</head>
|
|
5590
5920
|
<body>
|
|
@@ -5592,7 +5922,8 @@
|
|
|
5592
5922
|
<!-- Auth Gate -->
|
|
5593
5923
|
<div id="auth-gate">
|
|
5594
5924
|
<div class="auth-box">
|
|
5595
|
-
<
|
|
5925
|
+
<div class="brand-logo" aria-hidden="true"></div>
|
|
5926
|
+
<h1>OAR</h1>
|
|
5596
5927
|
<p>Enter your access token to connect</p>
|
|
5597
5928
|
<input id="token-input" type="password" placeholder="Access token" autofocus onkeydown="if(event.key==='Enter')window.__copilotAuthConnect(event)">
|
|
5598
5929
|
<button onclick="window.__copilotAuthConnect(event)">Connect</button>
|
|
@@ -5600,7 +5931,87 @@
|
|
|
5600
5931
|
</div>
|
|
5601
5932
|
</div>
|
|
5602
5933
|
|
|
5603
|
-
<div id="startup-loading" role="status" aria-live="polite">Connecting to OAR…</div>
|
|
5934
|
+
<div id="startup-loading" role="status" aria-live="polite"><div class="brand-logo" aria-hidden="true"></div><div class="startup-label">Connecting to OAR…</div></div>
|
|
5935
|
+
<script>
|
|
5936
|
+
// The start screen's hand-over from the system launch screen, and its two
|
|
5937
|
+
// ways out: into the app (finish) or into the sign-in box (toAuth). Only the
|
|
5938
|
+
// installed app animates and makes its exits wait for the animation; a
|
|
5939
|
+
// browser tab has no launch screen to continue from and leaves at once.
|
|
5940
|
+
window.__startupSplash = (() => {
|
|
5941
|
+
const root = document.getElementById('startup-loading');
|
|
5942
|
+
const logo = root.querySelector('.brand-logo');
|
|
5943
|
+
const label = root.querySelector('.startup-label');
|
|
5944
|
+
const media = (query) => { try { return window.matchMedia(query).matches; } catch { return false; } };
|
|
5945
|
+
const installed = media('(display-mode: standalone)') || media('(display-mode: fullscreen)')
|
|
5946
|
+
|| window.navigator.standalone === true;
|
|
5947
|
+
const animated = installed && !media('(prefers-reduced-motion: reduce)');
|
|
5948
|
+
const wait = (ms) => new Promise((resolve) => { setTimeout(resolve, ms); });
|
|
5949
|
+
let done = Promise.resolve();
|
|
5950
|
+
if (animated) {
|
|
5951
|
+
// Android draws its launch icon around the centre of the whole screen,
|
|
5952
|
+
// 275 px across for a maskable icon, which makes the otter 155 px wide.
|
|
5953
|
+
// The tile used here carries the otter at 373/512 of its width.
|
|
5954
|
+
const LAUNCH_TILE_PX = 155.2 / (373 / 512);
|
|
5955
|
+
// The page only knows the sum of the system bars; a three-button
|
|
5956
|
+
// navigation bar (48 px) is assumed for the split.
|
|
5957
|
+
const NAV_BAR_PX = 48;
|
|
5958
|
+
const rect = logo.getBoundingClientRect();
|
|
5959
|
+
const bars = Math.max(0, (window.screen?.height || window.innerHeight) - window.innerHeight);
|
|
5960
|
+
const topBar = Math.max(0, bars - NAV_BAR_PX);
|
|
5961
|
+
let targetY = (window.screen?.height || window.innerHeight) / 2 - topBar;
|
|
5962
|
+
// An implausible reading (split screen, a desktop window) falls back to
|
|
5963
|
+
// the middle of the page.
|
|
5964
|
+
if (!(Math.abs(targetY - window.innerHeight / 2) <= 60)) targetY = window.innerHeight / 2;
|
|
5965
|
+
root.style.setProperty('--startup-dx', `${window.innerWidth / 2 - (rect.left + rect.width / 2)}px`);
|
|
5966
|
+
root.style.setProperty('--startup-dy', `${targetY - (rect.top + rect.height / 2)}px`);
|
|
5967
|
+
root.style.setProperty('--startup-scale', String(LAUNCH_TILE_PX / rect.width));
|
|
5968
|
+
root.classList.add('is-animated');
|
|
5969
|
+
// Rest 200 ms, settle 500 ms; the label's fade ends at 850 ms.
|
|
5970
|
+
done = wait(850);
|
|
5971
|
+
}
|
|
5972
|
+
let left = null;
|
|
5973
|
+
const leave = (run) => {
|
|
5974
|
+
// Removed whatever happens on the way out: a start screen left over
|
|
5975
|
+
// the page would cover the whole app.
|
|
5976
|
+
if (!left) {
|
|
5977
|
+
left = (animated && root.isConnected ? done.then(run) : Promise.resolve())
|
|
5978
|
+
.catch(() => {})
|
|
5979
|
+
.then(() => { root.remove(); });
|
|
5980
|
+
}
|
|
5981
|
+
return left;
|
|
5982
|
+
};
|
|
5983
|
+
return {
|
|
5984
|
+
animated,
|
|
5985
|
+
// Into the app: fade the start screen away over the page below it.
|
|
5986
|
+
finish: () => leave(async () => {
|
|
5987
|
+
root.classList.add('is-leaving');
|
|
5988
|
+
await wait(150);
|
|
5989
|
+
}),
|
|
5990
|
+
// Into the sign-in box: the logo travels on into the box's own logo
|
|
5991
|
+
// place, shrinking to its size, and the box fades in around it.
|
|
5992
|
+
toAuth: () => leave(async () => {
|
|
5993
|
+
const box = document.querySelector('#auth-gate .auth-box');
|
|
5994
|
+
const target = box?.querySelector('.brand-logo')?.getBoundingClientRect();
|
|
5995
|
+
if (!box || !target || !target.width) return;
|
|
5996
|
+
box.style.opacity = '0';
|
|
5997
|
+
root.style.background = 'transparent';
|
|
5998
|
+
label.style.animation = 'none';
|
|
5999
|
+
label.style.opacity = '0';
|
|
6000
|
+
const from = logo.getBoundingClientRect();
|
|
6001
|
+
logo.style.animation = 'none';
|
|
6002
|
+
logo.style.transition = 'transform 450ms cubic-bezier(0.22, 1, 0.36, 1)';
|
|
6003
|
+
logo.style.transform = `translate(${target.left + target.width / 2 - (from.left + from.width / 2)}px, `
|
|
6004
|
+
+ `${target.top + target.height / 2 - (from.top + from.height / 2)}px) scale(${target.width / from.width})`;
|
|
6005
|
+
await wait(450);
|
|
6006
|
+
box.style.transition = 'opacity 300ms ease';
|
|
6007
|
+
box.style.opacity = '1';
|
|
6008
|
+
await wait(300);
|
|
6009
|
+
box.style.transition = '';
|
|
6010
|
+
box.style.opacity = '';
|
|
6011
|
+
}),
|
|
6012
|
+
};
|
|
6013
|
+
})();
|
|
6014
|
+
</script>
|
|
5604
6015
|
|
|
5605
6016
|
<!-- Sidebar overlay (mobile) -->
|
|
5606
6017
|
<div id="sidebar-overlay" onclick="closeSidebar()"></div>
|
|
@@ -5643,6 +6054,7 @@
|
|
|
5643
6054
|
</form>
|
|
5644
6055
|
</div>
|
|
5645
6056
|
<div id="chat-header-actions">
|
|
6057
|
+
<button id="pinned-messages-btn" class="header-icon-btn has-count" type="button" title="Pinned messages" aria-label="Pinned messages" hidden>📍<span class="header-icon-count"></span></button>
|
|
5646
6058
|
<button id="message-search-btn" class="header-icon-btn" onclick="openMessageSearchModal()" title="Search messages" aria-label="Search messages">🔍</button>
|
|
5647
6059
|
<button id="context-btn" class="header-icon-btn" onclick="showContext()" title="Check current context" aria-label="Check current context">🧠</button>
|
|
5648
6060
|
<div id="chat-actions-menu-wrap">
|
|
@@ -6855,25 +7267,33 @@
|
|
|
6855
7267
|
</div>
|
|
6856
7268
|
</div>
|
|
6857
7269
|
|
|
6858
|
-
<!--
|
|
7270
|
+
<!-- Fullscreen file viewer. The content fills the screen; the chrome bars
|
|
7271
|
+
float over it and a tap on the content shows or hides them. -->
|
|
6859
7272
|
<div id="file-preview-modal" aria-hidden="true">
|
|
6860
7273
|
<div class="file-preview-dialog" role="dialog" aria-modal="true" aria-label="File preview">
|
|
6861
|
-
<div class="file-preview-
|
|
6862
|
-
|
|
6863
|
-
<
|
|
7274
|
+
<div id="file-preview-body" class="file-preview-body"></div>
|
|
7275
|
+
<div id="file-preview-chrome-top" class="file-preview-chrome file-preview-chrome-top">
|
|
7276
|
+
<div class="file-preview-header">
|
|
7277
|
+
<div id="file-preview-title" class="file-preview-title">File preview</div>
|
|
7278
|
+
<button class="file-preview-close" onclick="closeFilePreview()" aria-label="Close preview">×</button>
|
|
7279
|
+
</div>
|
|
7280
|
+
<div class="file-preview-toolbar">
|
|
7281
|
+
<button id="file-preview-back" class="file-preview-btn" type="button" onclick="goBackFilePreview()" hidden disabled>← Back</button>
|
|
7282
|
+
<button id="file-preview-mode-preview" class="file-preview-btn active" onclick="setFilePreviewMode('preview')">Preview</button>
|
|
7283
|
+
<button id="file-preview-mode-raw" class="file-preview-btn" onclick="setFilePreviewMode('raw')">Raw</button>
|
|
7284
|
+
<button id="file-preview-html-toggle" class="file-preview-btn" onclick="toggleFilePreviewHtml()">Show literal HTML</button>
|
|
7285
|
+
<a id="file-preview-open-raw" class="file-preview-btn" download>Download</a>
|
|
7286
|
+
<button id="file-preview-copy-btn" class="file-preview-btn" type="button" onclick="copyFilePreviewImage()" hidden>Copy</button>
|
|
7287
|
+
<button id="file-preview-annotate-btn" class="file-preview-btn" type="button" onclick="openAnnotateEditorForPreview()" hidden>🖍️ Annotate</button>
|
|
7288
|
+
<span id="file-preview-warning" class="file-preview-warning">⚠ Safe layout HTML enabled; active content is blocked</span>
|
|
7289
|
+
</div>
|
|
7290
|
+
<div id="file-preview-meta" class="file-preview-meta"></div>
|
|
6864
7291
|
</div>
|
|
6865
|
-
<div class="file-preview-
|
|
6866
|
-
<button id="file-preview-
|
|
6867
|
-
<
|
|
6868
|
-
<button id="file-preview-
|
|
6869
|
-
<button id="file-preview-html-toggle" class="file-preview-btn" onclick="toggleFilePreviewHtml()">Show literal HTML</button>
|
|
6870
|
-
<a id="file-preview-open-raw" class="file-preview-btn" download>Download</a>
|
|
6871
|
-
<button id="file-preview-copy-btn" class="file-preview-btn" type="button" onclick="copyFilePreviewImage()" hidden>Copy</button>
|
|
6872
|
-
<button id="file-preview-annotate-btn" class="file-preview-btn" type="button" onclick="openAnnotateEditorForPreview()" hidden>🖍️ Annotate</button>
|
|
6873
|
-
<span id="file-preview-warning" class="file-preview-warning">⚠ Safe layout HTML enabled; active content is blocked</span>
|
|
7292
|
+
<div id="file-preview-chrome-bottom" class="file-preview-chrome file-preview-chrome-bottom" hidden>
|
|
7293
|
+
<button id="file-preview-prev" class="file-preview-btn file-preview-nav" type="button" aria-label="Previous file">‹</button>
|
|
7294
|
+
<span id="file-preview-position" class="file-preview-position"></span>
|
|
7295
|
+
<button id="file-preview-next" class="file-preview-btn file-preview-nav" type="button" aria-label="Next file">›</button>
|
|
6874
7296
|
</div>
|
|
6875
|
-
<div id="file-preview-meta" class="file-preview-meta"></div>
|
|
6876
|
-
<div id="file-preview-body" class="file-preview-body"></div>
|
|
6877
7297
|
</div>
|
|
6878
7298
|
</div>
|
|
6879
7299
|
|
|
@@ -6908,8 +7328,8 @@
|
|
|
6908
7328
|
|
|
6909
7329
|
<script>
|
|
6910
7330
|
window.__loadCopilotApp().catch((error) => {
|
|
6911
|
-
document.getElementById('startup-loading')?.remove();
|
|
6912
7331
|
document.getElementById('auth-gate').style.display = 'flex';
|
|
7332
|
+
void window.__startupSplash.toAuth();
|
|
6913
7333
|
const errorEl = document.getElementById('auth-error');
|
|
6914
7334
|
if (errorEl) errorEl.textContent = `App failed to initialize: ${String(error?.message || error || 'unknown error')}`;
|
|
6915
7335
|
});
|