@k2b/cloud 0.38.0 → 0.39.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.
- package/package.json +6 -3
- package/scripts/build-canvas-workers.ts +109 -29
- package/scripts/build.ts +3 -5
- package/src/_internal/app-path.ts +25 -0
- package/src/_internal/app-presentation.ts +4 -0
- package/src/_internal/canvas-worker.ts +5 -3
- package/src/_internal/define-app.ts +28 -2
- package/src/_internal/panel-cookie.ts +39 -0
- package/src/_internal/registry-validation.ts +21 -26
- package/src/_internal/runtime-context.ts +1 -0
- package/src/access/PermissionEditor.browser-harness.tsx +57 -38
- package/src/access/PermissionEditor.tsx +30 -5
- package/src/access/ResourceApiKeys.tsx +5 -1
- package/src/ai/approval-routes.ts +5 -2
- package/src/ai/approvals.ts +139 -28
- package/src/ai/browser.ts +10 -0
- package/src/ai/chat/action-sentences.ts +1 -44
- package/src/ai/chat/turn-view.tsx +35 -3
- package/src/ai/chat/view-block.tsx +7 -2
- package/src/ai/chat-task-contracts.ts +28 -3
- package/src/ai/chat-task-reasons-migrate.ts +68 -0
- package/src/ai/chat-task-reasons.ts +92 -0
- package/src/ai/chat-tasks.ts +396 -66
- package/src/ai/index.ts +12 -0
- package/src/ai/migrate.ts +28 -2
- package/src/ai/request-context.ts +7 -1
- package/src/ai/routes.ts +27 -1
- package/src/ai/runtime.ts +12 -27
- package/src/ai/store.ts +5 -3
- package/src/ai/timeline.ts +9 -1
- package/src/ai/tool-wording.ts +45 -0
- package/src/ai/turn-abort.ts +33 -0
- package/src/ai/types.ts +12 -1
- package/src/api/admin-core-settings.ts +190 -139
- package/src/api/admin-outgoing-mail.ts +4 -2
- package/src/api/app-approval.ts +9 -2
- package/src/api/approvals.ts +1 -1
- package/src/api/index.ts +2 -1
- package/src/browser/CloudResourceSearch.tsx +10 -6
- package/src/browser/app-approval.ts +7 -2
- package/src/browser/composer-catalog.ts +103 -0
- package/src/browser/composer-insert-messages.ts +31 -0
- package/src/browser/composer-insert.ts +150 -0
- package/src/browser/panel-messages.ts +24 -0
- package/src/browser/panel-slot.ts +354 -0
- package/src/browser/panel.ts +28 -0
- package/src/browser/resource-picker.tsx +12 -1
- package/src/contracts/app-approval.ts +49 -9
- package/src/contracts/app.ts +14 -0
- package/src/contracts/registry.ts +3 -1
- package/src/contracts/shared.ts +2 -0
- package/src/server/services/access.ts +5 -1
- package/src/services/accounts/posix.ts +2 -1
- package/src/services/app-approval.ts +158 -38
- package/src/services/approvals/device.ts +2 -0
- package/src/services/index.ts +21 -3
- package/src/services/notifications/browser.ts +16 -5
- package/src/services/pdf/image-protocol.ts +135 -0
- package/src/services/pdf/image-variants.ts +124 -0
- package/src/services/pdf/image-worker.ts +31 -0
- package/src/services/pdf/image.ts +109 -0
- package/src/services/pdf/index.ts +9 -0
- package/src/services/settings/api.ts +11 -6
- package/src/services/settings/app.ts +5 -4
- package/src/services/settings/core-settings.ts +4 -3
- package/src/services/settings/index.ts +19 -17
- package/src/services/settings/namespace.ts +2 -1
- package/src/services/settings/store.ts +83 -11
- package/src/shared/ai-platform-prompt.ts +1 -1
- package/src/shared/app-presentation.ts +1 -0
- package/src/shared/redirect.ts +12 -0
- package/src/ssr/CloudShell.island.tsx +86 -9
- package/src/ssr/Layout.tsx +42 -9
- package/src/ssr/LayoutHeader.tsx +15 -9
- package/src/ssr/LayoutHelp.tsx +1 -1
- package/src/ssr/LayoutRail.tsx +2 -2
- package/src/ssr/LayoutTabBar.tsx +29 -0
- package/src/ssr/PanelLayout.tsx +27 -0
- package/src/ssr/PanelRuntime.island.tsx +87 -0
- package/src/ssr/PanelSlot.tsx +108 -0
- package/src/ssr/PanelTrigger.tsx +49 -0
- package/src/ssr/ShellRuntime.island.tsx +60 -5
- package/src/ssr/app-appearance.ts +3 -2
- package/src/ssr/app-color.ts +100 -0
- package/src/ssr/app-launchpad.tsx +18 -1
- package/src/ssr/app-navigation.ts +15 -0
- package/src/ssr/compact-shell.baseline.json +5270 -0
- package/src/ssr/compact-shell.browser-harness.ts +431 -0
- package/src/ssr/index.ts +3 -0
- package/src/ssr/panel-slot.browser-harness.tsx +110 -0
- package/src/ssr/platform-messages.ts +30 -0
- package/src/ssr/rail-navigation.ts +44 -0
- package/src/ssr/shell-data.tsx +4 -0
- package/src/ssr/shell-shortcuts.ts +13 -2
- package/src/styles/effects.css +3 -2
- package/src/styles/global.css +269 -55
- package/src/styles/panel.css +373 -0
- package/src/styles/tokens.css +23 -6
- package/src/styles/utilities-navigation.css +5 -21
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import type { platformMessages } from "./platform-messages";
|
|
2
2
|
|
|
3
|
-
export const SHELL_SHORTCUT_ORDER = ["cloud.search", "cloud.help"] as const;
|
|
3
|
+
export const SHELL_SHORTCUT_ORDER = ["cloud.search", "cloud.help", "cloud.panel"] as const;
|
|
4
4
|
/** The shell's always-present shortcuts, without their actions; ShellRuntime adds the actions, Help renders them before the browser reports commands. */
|
|
5
|
-
export const shellShortcuts = (t: ReturnType<typeof platformMessages.resolve>["t"]) => [
|
|
5
|
+
export const shellShortcuts = (t: ReturnType<typeof platformMessages.resolve>["t"], options: { panel?: boolean } = {}) => [
|
|
6
6
|
{ id: "cloud.search", title: t.openGlobalSearch, description: t.globalSearchDescription, icon: "ti ti-search", shortcut: "mod+k" },
|
|
7
7
|
{ id: "cloud.help", title: t.openShortcutHelp, description: t.shortcutHelpDescription, icon: "ti ti-help-circle", shortcut: "shift+/" },
|
|
8
|
+
...(options.panel
|
|
9
|
+
? [
|
|
10
|
+
{
|
|
11
|
+
id: "cloud.panel",
|
|
12
|
+
title: t.togglePanel,
|
|
13
|
+
description: t.togglePanelDescription,
|
|
14
|
+
icon: "ti ti-layout-sidebar-right",
|
|
15
|
+
shortcut: "mod+j",
|
|
16
|
+
},
|
|
17
|
+
]
|
|
18
|
+
: []),
|
|
8
19
|
];
|
package/src/styles/effects.css
CHANGED
|
@@ -880,14 +880,15 @@
|
|
|
880
880
|
flex-direction: column;
|
|
881
881
|
gap: 0.5rem;
|
|
882
882
|
border-radius: var(--ui-radius-surface);
|
|
883
|
-
|
|
883
|
+
/* Both fills are computed with the app ink, so its label stays at AA. */
|
|
884
|
+
background: var(--ui-app-selected);
|
|
884
885
|
padding: 1.25rem 1.25rem 1.375rem;
|
|
885
886
|
text-decoration: none;
|
|
886
887
|
transition: background-color 140ms ease;
|
|
887
888
|
}
|
|
888
889
|
|
|
889
890
|
.help-article-next:hover {
|
|
890
|
-
background:
|
|
891
|
+
background: var(--ui-app-selected-nav);
|
|
891
892
|
}
|
|
892
893
|
|
|
893
894
|
.help-article-next-label {
|
package/src/styles/global.css
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
@import "./input.css";
|
|
33
33
|
@import "./resource-search.css";
|
|
34
34
|
@import "./file-chooser.css";
|
|
35
|
+
@import "./panel.css";
|
|
35
36
|
|
|
36
37
|
@theme {
|
|
37
38
|
--color-dark: rgb(9 9 11);
|
|
@@ -52,17 +53,19 @@
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
.cloud-app-canvas {
|
|
56
|
+
--cloud-canvas-strength: var(--app-canvas-strength, 0%);
|
|
57
|
+
--cloud-canvas-dark-strength: var(--app-canvas-dark-strength, 0%);
|
|
55
58
|
padding: var(--ui-space-shell);
|
|
56
59
|
background: linear-gradient(
|
|
57
60
|
var(--app-canvas-angle, 135deg),
|
|
58
|
-
color-mix(in srgb, var(--app-canvas-from, var(--color-zinc-50)) var(--
|
|
61
|
+
color-mix(in srgb, var(--app-canvas-from, var(--color-zinc-50)) var(--cloud-canvas-strength), var(--color-zinc-50)),
|
|
59
62
|
color-mix(
|
|
60
63
|
in srgb,
|
|
61
|
-
var(--app-canvas-via, var(--app-canvas-from, var(--color-zinc-50))) var(--
|
|
64
|
+
var(--app-canvas-via, var(--app-canvas-from, var(--color-zinc-50))) var(--cloud-canvas-strength),
|
|
62
65
|
var(--color-zinc-50)
|
|
63
66
|
)
|
|
64
67
|
50%,
|
|
65
|
-
color-mix(in srgb, var(--app-canvas-to, var(--color-zinc-50)) var(--
|
|
68
|
+
color-mix(in srgb, var(--app-canvas-to, var(--color-zinc-50)) var(--cloud-canvas-strength), var(--color-zinc-50))
|
|
66
69
|
);
|
|
67
70
|
}
|
|
68
71
|
|
|
@@ -88,12 +91,46 @@ html:has(.cloud-app-canvas[data-layout-full-page="true"]) {
|
|
|
88
91
|
.dark .cloud-app-canvas {
|
|
89
92
|
background: linear-gradient(
|
|
90
93
|
var(--app-canvas-angle, 135deg),
|
|
91
|
-
color-mix(in srgb, var(--app-canvas-from, var(--ui-canvas)) var(--
|
|
92
|
-
color-mix(in srgb, var(--app-canvas-via-dark, var(--ui-canvas)) var(--
|
|
93
|
-
color-mix(in srgb, var(--app-canvas-to, var(--ui-canvas)) var(--
|
|
94
|
+
color-mix(in srgb, var(--app-canvas-from, var(--ui-canvas)) var(--cloud-canvas-dark-strength), var(--ui-canvas)),
|
|
95
|
+
color-mix(in srgb, var(--app-canvas-via-dark, var(--ui-canvas)) var(--cloud-canvas-dark-strength), var(--ui-canvas)) 50%,
|
|
96
|
+
color-mix(in srgb, var(--app-canvas-to, var(--ui-canvas)) var(--cloud-canvas-dark-strength), var(--ui-canvas))
|
|
94
97
|
);
|
|
95
98
|
}
|
|
96
99
|
|
|
100
|
+
/* The shell's own colored surfaces, the tablet rail's container and the
|
|
101
|
+
phone's bottom bar, carry the app color alone in a narrow shape. A white
|
|
102
|
+
middle, the canvas's default `via`, would make their edge vanish halfway,
|
|
103
|
+
so they mix the middle from the app's two colors and take a stronger tint:
|
|
104
|
+
34 % light and 22 % dark, with a 16 % and 12 % middle. A page without an app
|
|
105
|
+
color gets a neutral tint. The app declaration stays the same. */
|
|
106
|
+
.cloud-app-canvas {
|
|
107
|
+
--cloud-shell-strength: 34%;
|
|
108
|
+
--cloud-shell-via-strength: 16%;
|
|
109
|
+
--cloud-shell-gradient: linear-gradient(
|
|
110
|
+
160deg,
|
|
111
|
+
color-mix(in srgb, var(--app-canvas-from, var(--color-zinc-400)) var(--cloud-shell-strength), var(--ui-canvas)),
|
|
112
|
+
color-mix(
|
|
113
|
+
in srgb,
|
|
114
|
+
color-mix(in srgb, var(--app-canvas-from, var(--color-zinc-400)) 45%, var(--app-canvas-to, var(--color-zinc-400)))
|
|
115
|
+
var(--cloud-shell-via-strength),
|
|
116
|
+
var(--ui-canvas)
|
|
117
|
+
)
|
|
118
|
+
55%,
|
|
119
|
+
color-mix(in srgb, var(--app-canvas-to, var(--color-zinc-400)) var(--cloud-shell-strength), var(--ui-canvas))
|
|
120
|
+
);
|
|
121
|
+
/* On color the open item is the surface; on the surface it is tinted. Its
|
|
122
|
+
label is the app ink, which app-color.ts keeps at AA on the surface and on
|
|
123
|
+
the app's selection fills. */
|
|
124
|
+
--cloud-shell-selected: var(--k2b-surface);
|
|
125
|
+
--cloud-shell-selected-text: var(--ui-app-accent-text);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.dark .cloud-app-canvas {
|
|
129
|
+
--cloud-shell-strength: 22%;
|
|
130
|
+
--cloud-shell-via-strength: 12%;
|
|
131
|
+
--cloud-shell-selected: var(--ui-app-selected-nav);
|
|
132
|
+
}
|
|
133
|
+
|
|
97
134
|
:root {
|
|
98
135
|
color-scheme: light;
|
|
99
136
|
}
|
|
@@ -243,6 +280,21 @@ body {
|
|
|
243
280
|
color: rgb(244 244 245); /* zinc-100 */
|
|
244
281
|
}
|
|
245
282
|
|
|
283
|
+
/* The root paints the screen's page color as well. @k2b/ui makes it the
|
|
284
|
+
default `k2b-app-area` container, and containment stops a body background
|
|
285
|
+
from reaching the canvas that overscroll and short pages show. Zero
|
|
286
|
+
specificity, so the PWA canvas style and syncThemeColor still win; screen
|
|
287
|
+
only, so paper stays white. */
|
|
288
|
+
@media screen {
|
|
289
|
+
:where(html) {
|
|
290
|
+
background-color: rgb(250 250 250); /* zinc-50 */
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
:where(html.dark) {
|
|
294
|
+
background-color: rgb(9 9 11); /* zinc-950 */
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
246
298
|
/* Pages of the mobile app paint the shell canvas, which their theme-color and
|
|
247
299
|
html background repeat for the status bar. */
|
|
248
300
|
html:has(.k2b-mobile-shell) body {
|
|
@@ -295,31 +347,37 @@ solid-island {
|
|
|
295
347
|
|
|
296
348
|
/* Cloud has three shell modes. Phones, below md, get the header and no rail.
|
|
297
349
|
From md, signed-in people get the 56 px rail instead of the header: up to
|
|
298
|
-
1023 px the
|
|
299
|
-
from 1024 px
|
|
300
|
-
and the card from md. Each query is the exact opposite of
|
|
301
|
-
every width, also a fractional one under browser zoom,
|
|
302
|
-
one mode. */
|
|
350
|
+
1023 px the rail sits in a rounded container beside the app as a rounded
|
|
351
|
+
card, and from 1024 px the app is a card beside the rail. Anonymous pages
|
|
352
|
+
keep the header and the card from md. Each query is the exact opposite of
|
|
353
|
+
the next one, so every width, also a fractional one under browser zoom,
|
|
354
|
+
falls into exactly one mode. */
|
|
303
355
|
@media not all and (min-width: 768px) {
|
|
304
356
|
.cloud-app-canvas {
|
|
305
357
|
padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);
|
|
306
358
|
}
|
|
307
|
-
/*
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
.
|
|
359
|
+
/* Anonymous pages: the flat surface replaces the app gradient in both
|
|
360
|
+
themes, and the document is the page scroller. The dark selector matches
|
|
361
|
+
the specificity of the dark gradient above. */
|
|
362
|
+
.cloud-app-canvas:not([data-layout-authenticated]),
|
|
363
|
+
.dark .cloud-app-canvas:not([data-layout-authenticated]) {
|
|
311
364
|
background: var(--ui-surface);
|
|
312
365
|
}
|
|
313
|
-
/*
|
|
314
|
-
|
|
315
|
-
inner scrolling
|
|
316
|
-
|
|
366
|
+
/* Reserve the page scroller's gutter so short and long pages keep the same
|
|
367
|
+
width: the document's, or the card's for signed-in people. Full-page
|
|
368
|
+
surfaces, workspaces, and scroll areas own their inner scrolling, so the
|
|
369
|
+
card reserves no second gutter beside theirs. */
|
|
370
|
+
html:has(.cloud-app-canvas:not([data-layout-full-page], [data-layout-authenticated])) { scrollbar-gutter: stable; }
|
|
371
|
+
.cloud-app-canvas[data-layout-authenticated]:not([data-layout-full-page])
|
|
372
|
+
.layout-content-main:not(:has(> .k2b-scroll-area, > .k2b-app-workspace, > :is(solid-island, solid-client) > .k2b-app-workspace)) {
|
|
373
|
+
scrollbar-gutter: stable;
|
|
374
|
+
}
|
|
317
375
|
/* A page that scrolls its content in a ScrollArea grows with it here, so
|
|
318
376
|
the area does not scroll and reserves no second gutter beside the
|
|
319
377
|
document's. From md, `main` bounds its height and it scrolls. */
|
|
320
|
-
.cloud-app-canvas:not([data-layout-full-page]) .layout-content-main > .k2b-scroll-area { scrollbar-gutter: auto; }
|
|
378
|
+
.cloud-app-canvas:not([data-layout-full-page], [data-layout-authenticated]) .layout-content-main > .k2b-scroll-area { scrollbar-gutter: auto; }
|
|
321
379
|
.cloud-app-canvas > .layout-shell-content { gap: 0; }
|
|
322
|
-
.cloud-app-canvas .layout-header {
|
|
380
|
+
.cloud-app-canvas:not([data-layout-authenticated]) .layout-header {
|
|
323
381
|
border-radius: 0; border: 0; border-bottom: 1px solid var(--ui-border);
|
|
324
382
|
box-shadow: none !important;
|
|
325
383
|
}
|
|
@@ -329,6 +387,145 @@ solid-island {
|
|
|
329
387
|
--k2b-toast-offset-top: 2.875rem;
|
|
330
388
|
}
|
|
331
389
|
|
|
390
|
+
/* Signed-in phones: the app gradient is the page. The title row sits on it
|
|
391
|
+
without a line, and the content is one soft card with 28 px top corners
|
|
392
|
+
that scrolls itself, so its rounded edge stays while it scrolls. The bar
|
|
393
|
+
with the person's apps floats over the card's end; the card keeps that
|
|
394
|
+
space free at its end and in its scroll padding, so the bar never covers
|
|
395
|
+
the last row or a focused control. The window never scrolls. */
|
|
396
|
+
.cloud-app-canvas[data-layout-authenticated] {
|
|
397
|
+
/* Text sits on the gradient here, so its tint stays at most 30 % light
|
|
398
|
+
and 15 % dark: then the title and controls read at 4.5:1 on any app
|
|
399
|
+
color. Built-in apps declare less. */
|
|
400
|
+
--cloud-canvas-strength: min(var(--app-canvas-strength, 0%), 30%);
|
|
401
|
+
--cloud-canvas-dark-strength: min(var(--app-canvas-dark-strength, 0%), 15%);
|
|
402
|
+
height: 100dvh;
|
|
403
|
+
overflow: hidden;
|
|
404
|
+
padding-bottom: 0;
|
|
405
|
+
}
|
|
406
|
+
.cloud-app-canvas[data-layout-authenticated] .layout-header {
|
|
407
|
+
border: 0;
|
|
408
|
+
border-radius: 0;
|
|
409
|
+
background: transparent;
|
|
410
|
+
box-shadow: none !important;
|
|
411
|
+
}
|
|
412
|
+
.cloud-app-canvas[data-layout-authenticated] .layout-header .font-semibold {
|
|
413
|
+
font-size: 1.125rem;
|
|
414
|
+
}
|
|
415
|
+
/* Content wider than the card scrolls sideways in it, as from md; the
|
|
416
|
+
rounded corners still clip. */
|
|
417
|
+
.cloud-app-canvas[data-layout-authenticated] .layout-content-main {
|
|
418
|
+
overflow: auto;
|
|
419
|
+
border-top: 1px solid transparent;
|
|
420
|
+
border-radius: 1.75rem 1.75rem 0 0;
|
|
421
|
+
background: var(--k2b-surface);
|
|
422
|
+
box-shadow: 0 -10px 30px -24px rgb(15 23 19 / 0.3);
|
|
423
|
+
}
|
|
424
|
+
/* Dark: a faint top edge keeps the card apart from the dark gradient. */
|
|
425
|
+
.dark .cloud-app-canvas[data-layout-authenticated] .layout-content-main {
|
|
426
|
+
border-top-color: var(--k2b-border);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* The bar and the room it takes switch with `md`, as the header and the rail
|
|
431
|
+
do, so Home stays reachable also where a larger default font size moves
|
|
432
|
+
that switch above 768 px. */
|
|
433
|
+
.cloud-app-canvas[data-layout-authenticated] {
|
|
434
|
+
--cloud-tab-bar-height: 3.875rem;
|
|
435
|
+
--cloud-tab-bar-end: calc(env(safe-area-inset-bottom, 0px) + 0.5rem);
|
|
436
|
+
--cloud-tab-bar-space: calc(var(--cloud-tab-bar-end) + var(--cloud-tab-bar-height) + 0.75rem);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/* A page whose app has its own navigation (a mailbox, a Space, a book) makes
|
|
440
|
+
its title the menu "Name ⌄" on phones. It opens the same app menu as the
|
|
441
|
+
menu button beside it. The navigation's server-rendered snapshot decides,
|
|
442
|
+
so the first frame already shows the right title. */
|
|
443
|
+
.layout-header-title-menu {
|
|
444
|
+
display: none;
|
|
445
|
+
min-width: 0;
|
|
446
|
+
min-height: 2rem;
|
|
447
|
+
align-items: center;
|
|
448
|
+
gap: 0.25rem;
|
|
449
|
+
margin-inline-start: -0.5rem;
|
|
450
|
+
padding: 0 0.5rem;
|
|
451
|
+
border-radius: var(--k2b-radius-control);
|
|
452
|
+
color: inherit;
|
|
453
|
+
background: transparent;
|
|
454
|
+
cursor: pointer;
|
|
455
|
+
}
|
|
456
|
+
.layout-header-title-menu:hover {
|
|
457
|
+
background: color-mix(in srgb, currentColor 8%, transparent);
|
|
458
|
+
}
|
|
459
|
+
.layout-header-title-menu:focus-visible {
|
|
460
|
+
outline: var(--k2b-focus-width) solid var(--k2b-focus-ring);
|
|
461
|
+
outline-offset: 1px;
|
|
462
|
+
}
|
|
463
|
+
.layout-header-title-menu > i {
|
|
464
|
+
flex: none;
|
|
465
|
+
font-size: 1rem;
|
|
466
|
+
opacity: 0.7;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
@media not all and (min-width: 48rem) {
|
|
470
|
+
:root:has(script[data-cloud-workspace-navigation]) .layout-header-title {
|
|
471
|
+
display: none;
|
|
472
|
+
}
|
|
473
|
+
:root:has(script[data-cloud-workspace-navigation]) .layout-header-title-menu {
|
|
474
|
+
display: inline-flex;
|
|
475
|
+
}
|
|
476
|
+
.cloud-app-canvas[data-layout-authenticated] .layout-content-main {
|
|
477
|
+
padding-bottom: var(--cloud-tab-bar-space);
|
|
478
|
+
scroll-padding-bottom: var(--cloud-tab-bar-space);
|
|
479
|
+
}
|
|
480
|
+
.cloud-app-canvas[data-layout-authenticated] > .cloud-tab-bar {
|
|
481
|
+
position: fixed;
|
|
482
|
+
right: 0;
|
|
483
|
+
bottom: 0;
|
|
484
|
+
left: 0;
|
|
485
|
+
z-index: 10;
|
|
486
|
+
display: flex;
|
|
487
|
+
justify-content: center;
|
|
488
|
+
align-items: flex-end;
|
|
489
|
+
height: var(--cloud-tab-bar-space);
|
|
490
|
+
padding: 0 calc(env(safe-area-inset-right, 0px) + 0.75rem) var(--cloud-tab-bar-end) calc(env(safe-area-inset-left, 0px) + 0.75rem);
|
|
491
|
+
/* Content that scrolls past fades flat into the card above the bar and is
|
|
492
|
+
gone where the bar begins: no glass, and no text beside or under it. */
|
|
493
|
+
background: linear-gradient(to bottom, color-mix(in srgb, var(--k2b-surface) 0%, transparent), var(--k2b-surface) 0.75rem);
|
|
494
|
+
pointer-events: none;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/* The bar exists only on signed-in phones. Its row keeps the place of the
|
|
499
|
+
app's primary action free at its end. */
|
|
500
|
+
.cloud-tab-bar {
|
|
501
|
+
display: none;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.cloud-tab-bar__row {
|
|
505
|
+
display: flex;
|
|
506
|
+
width: 100%;
|
|
507
|
+
max-width: 33.75rem;
|
|
508
|
+
padding-inline-end: calc(var(--cloud-tab-bar-height) + 0.625rem);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/* The shell's live bar replaces the server's once the bar's content changes
|
|
512
|
+
in the browser. */
|
|
513
|
+
.cloud-tab-bar__row:has(.cloud-tab-bar__live) > .k2b-tab-bar {
|
|
514
|
+
display: none;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.cloud-tab-bar__row > div:has(> .cloud-tab-bar__live) {
|
|
518
|
+
display: contents;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.cloud-tab-bar .k2b-tab-bar {
|
|
522
|
+
flex: 1;
|
|
523
|
+
min-width: 0;
|
|
524
|
+
pointer-events: auto;
|
|
525
|
+
--k2b-tab-bar-background: var(--cloud-shell-gradient);
|
|
526
|
+
--k2b-tab-bar-color: var(--k2b-text);
|
|
527
|
+
--k2b-tab-bar-selected-background: var(--cloud-shell-selected);
|
|
528
|
+
--k2b-tab-bar-selected-color: var(--cloud-shell-selected-text);
|
|
332
529
|
}
|
|
333
530
|
|
|
334
531
|
/* Card: the outermost workspace is the app card, so it gets the card's radius
|
|
@@ -337,9 +534,7 @@ solid-island {
|
|
|
337
534
|
child fills `main` and scrolls inside itself, so `main` neither scrolls nor
|
|
338
535
|
clips: it keeps no scrollbar gutter beside the card, the card keeps the
|
|
339
536
|
shell spacing on every side, and its frame shadow shows in that spacing. A
|
|
340
|
-
workspace nested in other page content leaves `main` the page scroller.
|
|
341
|
-
signed-in people, the flush range below removes the frame again; it comes
|
|
342
|
-
later at equal specificity. */
|
|
537
|
+
workspace nested in other page content leaves `main` the page scroller. */
|
|
343
538
|
@media (min-width: 768px) {
|
|
344
539
|
.cloud-app-canvas .layout-content-main:has(> .k2b-app-workspace, > :is(solid-island, solid-client) > .k2b-app-workspace) {
|
|
345
540
|
overflow: visible;
|
|
@@ -354,42 +549,60 @@ solid-island {
|
|
|
354
549
|
}
|
|
355
550
|
}
|
|
356
551
|
|
|
357
|
-
/*
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
own surfaces fill the insets, and their content stays inside them. */
|
|
552
|
+
/* Tablet (I5): the rail sits in a rounded container filled with the app
|
|
553
|
+
color, and the app is a rounded card beside it, both 20 px round with 8 px
|
|
554
|
+
of neutral canvas on every side. The installed app's safe areas, such as
|
|
555
|
+
the status bar and the home indicator, add to that spacing, so nothing sits
|
|
556
|
+
under them. The outermost workspace is the card, as from 1024 px; a page
|
|
557
|
+
without a workspace shows its content on the canvas. On color the open app
|
|
558
|
+
is the surface color, and no separator divides the rail. */
|
|
365
559
|
@media (min-width: 768px) {
|
|
366
560
|
@media not all and (min-width: 1024px) {
|
|
367
561
|
.cloud-app-canvas[data-layout-authenticated] {
|
|
368
|
-
padding: 0
|
|
562
|
+
padding: calc(env(safe-area-inset-top, 0px) + 0.5rem) calc(env(safe-area-inset-right, 0px) + 0.5rem)
|
|
563
|
+
calc(env(safe-area-inset-bottom, 0px) + 0.5rem) calc(env(safe-area-inset-left, 0px) + 0.5rem);
|
|
564
|
+
background: var(--ui-canvas);
|
|
369
565
|
}
|
|
370
566
|
.cloud-app-canvas[data-layout-authenticated] > .layout-rail {
|
|
371
|
-
|
|
372
|
-
|
|
567
|
+
width: 3.75rem;
|
|
568
|
+
margin-inline-end: 0.5rem;
|
|
569
|
+
padding: 0 0 0.5rem;
|
|
570
|
+
border-radius: 1.25rem;
|
|
571
|
+
background-image: var(--cloud-shell-gradient) !important;
|
|
373
572
|
}
|
|
374
|
-
.cloud-app-canvas[data-layout-authenticated] > .layout-
|
|
375
|
-
|
|
376
|
-
padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) 0 0;
|
|
377
|
-
background: var(--k2b-surface);
|
|
573
|
+
.dark .cloud-app-canvas[data-layout-authenticated] > .layout-rail {
|
|
574
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--app-accent, var(--k2b-border)) 22%, var(--k2b-border));
|
|
378
575
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
.cloud-app-canvas[data-layout-authenticated] .layout-content-main:not(:has(.k2b-app-workspace)) {
|
|
382
|
-
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
576
|
+
.cloud-app-canvas[data-layout-authenticated] > .layout-rail .rail-item {
|
|
577
|
+
color: var(--k2b-text-secondary);
|
|
383
578
|
}
|
|
384
|
-
.cloud-app-canvas[data-layout-authenticated] .layout-
|
|
385
|
-
--k2b-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
box-shadow:
|
|
579
|
+
.cloud-app-canvas[data-layout-authenticated] > .layout-rail .rail-item:hover {
|
|
580
|
+
background: color-mix(in srgb, var(--k2b-surface) 45%, transparent);
|
|
581
|
+
}
|
|
582
|
+
.cloud-app-canvas[data-layout-authenticated] > .layout-rail .rail-item-active {
|
|
583
|
+
box-shadow: 0 1px 2px rgb(9 9 11 / 0.06);
|
|
584
|
+
}
|
|
585
|
+
.cloud-app-canvas[data-layout-authenticated] > .layout-rail .rail-item-active,
|
|
586
|
+
.cloud-app-canvas[data-layout-authenticated] > .layout-rail .rail-item-active:hover {
|
|
587
|
+
border-color: transparent;
|
|
588
|
+
background: var(--cloud-shell-selected);
|
|
589
|
+
color: var(--cloud-shell-selected-text);
|
|
590
|
+
}
|
|
591
|
+
.cloud-app-canvas[data-layout-authenticated] > .layout-rail [role="separator"] {
|
|
592
|
+
display: none;
|
|
389
593
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
594
|
+
/* The apps scroll in their own area; room above and below the first and
|
|
595
|
+
last app keeps a focus ring whole, also WebKit's wider one, without
|
|
596
|
+
moving an app. */
|
|
597
|
+
.cloud-app-canvas[data-layout-authenticated] > .layout-rail [data-cloud-rail-apps] {
|
|
598
|
+
margin-block: -0.375rem;
|
|
599
|
+
padding-block: 0.375rem;
|
|
600
|
+
}
|
|
601
|
+
.cloud-app-canvas[data-layout-authenticated] > .layout-shell-content {
|
|
602
|
+
gap: 0.5rem;
|
|
603
|
+
}
|
|
604
|
+
.cloud-app-canvas[data-layout-authenticated] .layout-content-main .k2b-app-workspace:not(.k2b-app-workspace *) {
|
|
605
|
+
--k2b-workspace-radius: 1.25rem;
|
|
393
606
|
}
|
|
394
607
|
}
|
|
395
608
|
}
|
|
@@ -401,7 +614,7 @@ solid-island {
|
|
|
401
614
|
rail starts at the window's left edge and moves down with the card. In the
|
|
402
615
|
installed app the card and the rail stay below the status bar; the card keeps
|
|
403
616
|
its spacing at the bottom, and its surfaces keep their content above the
|
|
404
|
-
home indicator
|
|
617
|
+
home indicator. */
|
|
405
618
|
@media (min-width: 1024px) {
|
|
406
619
|
.cloud-app-canvas[data-layout-authenticated] {
|
|
407
620
|
--cloud-card-inset: round(clamp(0.25rem, calc(0.25rem + (100vw - 64rem) / 104), 0.5rem), 1px);
|
|
@@ -429,7 +642,7 @@ solid-island {
|
|
|
429
642
|
(Cmd/Ctrl+\ or the sidebar buttons). The canvas carries the state from the
|
|
430
643
|
`cloud_shell_focus` cookie, so the server renders the same first frame.
|
|
431
644
|
Phones ignore it. The app area takes the app bar's place and starts at the
|
|
432
|
-
card spacing
|
|
645
|
+
card spacing. The button that
|
|
433
646
|
shows them again floats at the app area's bottom start corner; on touch
|
|
434
647
|
screens a drag from the 18 px strip at the left edge does the same. From
|
|
435
648
|
768 to 1023 px the navigation is already an overlay behind the
|
|
@@ -470,8 +683,9 @@ solid-island {
|
|
|
470
683
|
|
|
471
684
|
@media (min-width: 768px) {
|
|
472
685
|
@media not all and (min-width: 1024px) {
|
|
473
|
-
.cloud-app-canvas[data-layout-authenticated][data-shell-focus]
|
|
474
|
-
|
|
686
|
+
.cloud-app-canvas[data-layout-authenticated][data-shell-focus] .cloud-shell-show {
|
|
687
|
+
left: calc(env(safe-area-inset-left, 0px) + 1.25rem);
|
|
688
|
+
bottom: calc(env(safe-area-inset-bottom, 0px) + 1.25rem);
|
|
475
689
|
}
|
|
476
690
|
}
|
|
477
691
|
}
|