@k2b/cloud 0.30.0 → 0.32.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.
Files changed (171) hide show
  1. package/package.json +7 -4
  2. package/src/_internal/app-presentation.ts +22 -1
  3. package/src/_internal/capabilities.ts +2 -2
  4. package/src/_internal/define-app.ts +17 -5
  5. package/src/_internal/registry.ts +50 -6
  6. package/src/_internal/widget-declarations.ts +53 -0
  7. package/src/ai/app-skills.ts +1 -7
  8. package/src/ai/approval-routes.ts +40 -4
  9. package/src/ai/approvals.ts +68 -10
  10. package/src/ai/audio-tool.ts +2 -1
  11. package/src/ai/browser.ts +109 -0
  12. package/src/ai/capabilities.ts +6 -2
  13. package/src/ai/chart-block.ts +431 -81
  14. package/src/ai/chat/action-sentences.ts +13 -7
  15. package/src/ai/chat/blocks.tsx +64 -39
  16. package/src/ai/chat/builtin-tools.tsx +9 -7
  17. package/src/ai/chat/chart-block.tsx +218 -44
  18. package/src/ai/chat/explorer-block.tsx +276 -0
  19. package/src/ai/chat/file-tools.tsx +40 -1
  20. package/src/ai/chat/live-turn.browser-harness.tsx +4 -1
  21. package/src/ai/chat/message-actions.tsx +9 -1
  22. package/src/ai/chat/message-utils.ts +7 -0
  23. package/src/ai/chat/messages.ts +34 -0
  24. package/src/ai/chat/presentation.tsx +13 -3
  25. package/src/ai/chat/reference-text.tsx +34 -0
  26. package/src/ai/chat/tool-groups.ts +67 -12
  27. package/src/ai/chat/tool-names.ts +18 -2
  28. package/src/ai/chat/turn-layout.ts +8 -0
  29. package/src/ai/chat/turn-view.tsx +111 -32
  30. package/src/ai/chat/view-block.browser-harness.tsx +35 -0
  31. package/src/ai/chat/view-block.tsx +417 -0
  32. package/src/ai/chat/visual-tools.tsx +32 -53
  33. package/src/ai/chat/web-tools.tsx +95 -1
  34. package/src/ai/client/controller.ts +6 -3
  35. package/src/ai/client-tool-results.ts +9 -0
  36. package/src/ai/code-runtime-tools.ts +50 -17
  37. package/src/ai/code-source-contracts.ts +25 -21
  38. package/src/ai/code-source-tools.ts +3 -1
  39. package/src/ai/default-tool-contracts.ts +334 -91
  40. package/src/ai/default-tools.ts +87 -11
  41. package/src/ai/executor.ts +343 -45
  42. package/src/ai/fetch-file-tool.ts +41 -4
  43. package/src/ai/file-error.ts +2 -0
  44. package/src/ai/file-tools.ts +27 -7
  45. package/src/ai/files-store.ts +60 -0
  46. package/src/ai/firecrawl-tools.ts +87 -4
  47. package/src/ai/html-pdf-tool.ts +3 -2
  48. package/src/ai/http.ts +37 -12
  49. package/src/ai/index.ts +98 -0
  50. package/src/ai/local-tool-contracts.ts +244 -0
  51. package/src/ai/malformed-tool-call-repair.ts +144 -0
  52. package/src/ai/markdown-pdf-tool.ts +2 -1
  53. package/src/ai/migrate.ts +120 -2
  54. package/src/ai/openai-api-messages.ts +53 -0
  55. package/src/ai/openai-api-slots.ts +40 -0
  56. package/src/ai/openai-api.ts +383 -0
  57. package/src/ai/protocol.ts +28 -3
  58. package/src/ai/provider.ts +16 -4
  59. package/src/ai/request-context.ts +33 -0
  60. package/src/ai/routes.ts +99 -49
  61. package/src/ai/runtime.ts +90 -15
  62. package/src/ai/settings.ts +5 -0
  63. package/src/ai/skill-catalog.ts +20 -5
  64. package/src/ai/skill-format.ts +17 -7
  65. package/src/ai/skill-tool.ts +15 -5
  66. package/src/ai/skills-routes.ts +20 -6
  67. package/src/ai/skills.ts +26 -7
  68. package/src/ai/store.ts +91 -1
  69. package/src/ai/system-prompt.ts +69 -10
  70. package/src/ai/timeline.ts +3 -1
  71. package/src/ai/tool-audit.ts +11 -1
  72. package/src/ai/tools.ts +94 -3
  73. package/src/ai/types.ts +48 -0
  74. package/src/ai/view-block.ts +941 -0
  75. package/src/ai/vision-tool.ts +2 -1
  76. package/src/ai/web-provenance.ts +103 -0
  77. package/src/api/admin-identity.ts +6 -1
  78. package/src/api/app-approval-actions.ts +36 -0
  79. package/src/api/app-approval.ts +10 -2
  80. package/src/api/approvals-loader.ts +15 -0
  81. package/src/api/approvals.ts +120 -0
  82. package/src/api/capabilities.ts +3 -2
  83. package/src/api/index.ts +3 -0
  84. package/src/api/me.ts +12 -3
  85. package/src/api/widgets.ts +36 -5
  86. package/src/approvals/Approvals.browser-harness.tsx +16 -0
  87. package/src/approvals/Approvals.tsx +621 -0
  88. package/src/approvals/ApprovalsNavStatus.tsx +20 -0
  89. package/src/approvals/messages.ts +95 -0
  90. package/src/approvals/ui.ts +8 -0
  91. package/src/browser/CloudResourceSearch.tsx +1 -1
  92. package/src/browser/app-approval.ts +32 -0
  93. package/src/browser/choose-files.tsx +26 -13
  94. package/src/browser/command-shortcuts.ts +21 -6
  95. package/src/browser/commands.ts +13 -4
  96. package/src/browser/file-provider-error.ts +11 -0
  97. package/src/browser/file-providers.ts +3 -12
  98. package/src/browser/files.tsx +1 -2
  99. package/src/browser/live-connection.ts +2 -3
  100. package/src/browser/provider-list.ts +2 -1
  101. package/src/browser/save-files.tsx +13 -2
  102. package/src/browser/spotlight-position.ts +4 -1
  103. package/src/browser/widgets.ts +7 -5
  104. package/src/capabilities/executions.ts +3 -0
  105. package/src/cli/account.ts +21 -2
  106. package/src/cli/commands.ts +24 -2
  107. package/src/cli/index.ts +16 -0
  108. package/src/cli/plugin.ts +30 -0
  109. package/src/contracts/app-approval.ts +120 -0
  110. package/src/contracts/app.ts +34 -2
  111. package/src/contracts/approval-paths.ts +4 -0
  112. package/src/contracts/approvals.ts +81 -0
  113. package/src/contracts/command-path.ts +17 -0
  114. package/src/contracts/commands.ts +5 -15
  115. package/src/contracts/registry.ts +9 -1
  116. package/src/contracts/shared.ts +5 -0
  117. package/src/contracts/widgets.ts +47 -2
  118. package/src/desktop/solid.tsx +6 -7
  119. package/src/events/live-protocol.ts +2 -1
  120. package/src/events/live-server-message.ts +40 -0
  121. package/src/events/testing.ts +88 -0
  122. package/src/server/index.ts +3 -0
  123. package/src/server/middleware/auth.ts +11 -1
  124. package/src/server/request-timeout.ts +22 -0
  125. package/src/server/services/access.ts +4 -3
  126. package/src/server/widget-request.ts +27 -0
  127. package/src/services/app-approval.ts +103 -15
  128. package/src/services/approvals/cursor.ts +15 -0
  129. package/src/services/approvals/device.ts +112 -0
  130. package/src/services/approvals/index.ts +241 -0
  131. package/src/services/approvals/list.ts +120 -0
  132. package/src/services/approvals/migrate.ts +41 -0
  133. package/src/services/identity/models-only-auth.ts +25 -0
  134. package/src/services/notifications/email-frame.ts +16 -3
  135. package/src/services/service-account-credentials.ts +22 -10
  136. package/src/services/settings/core-settings.ts +27 -0
  137. package/src/shared/ai-platform-prompt.ts +26 -1
  138. package/src/shared/app-presentation.ts +11 -0
  139. package/src/shared/email-html.ts +46 -7
  140. package/src/shared/index.ts +6 -1
  141. package/src/shared/markdown/extensions/code.ts +14 -0
  142. package/src/shared/markdown/index.ts +4 -4
  143. package/src/ssr/AdminLayout.tsx +4 -7
  144. package/src/ssr/AppBadge.tsx +4 -4
  145. package/src/ssr/AppLaunchpad.island.tsx +1 -1
  146. package/src/ssr/AppLaunchpadPanel.tsx +4 -4
  147. package/src/ssr/Layout.tsx +38 -14
  148. package/src/ssr/LayoutFooter.tsx +1 -1
  149. package/src/ssr/LayoutHeader.tsx +17 -20
  150. package/src/ssr/LayoutHelp.tsx +1 -1
  151. package/src/ssr/LayoutRail.tsx +1 -2
  152. package/src/ssr/PageTrail.tsx +23 -0
  153. package/src/ssr/PageTrailRow.island.tsx +20 -0
  154. package/src/ssr/PageTrailRuntime.island.tsx +26 -0
  155. package/src/ssr/ProfilePreferences.island.tsx +7 -3
  156. package/src/ssr/RailApps.island.tsx +2 -2
  157. package/src/ssr/app-badges.ts +50 -6
  158. package/src/ssr/islands/index.ts +1 -0
  159. package/src/ssr/layout-runtime.ts +60 -0
  160. package/src/ssr/mobile-menu-history.ts +1 -1
  161. package/src/ssr/page-trail-runtime.ts +313 -0
  162. package/src/ssr/page-trail.browser-harness.tsx +265 -0
  163. package/src/ssr/page-trail.ts +59 -0
  164. package/src/ssr/platform-messages.ts +4 -0
  165. package/src/ssr/rail-navigation.ts +5 -3
  166. package/src/ssr/runtime.ts +6 -1
  167. package/src/styles/approvals.css +390 -0
  168. package/src/styles/effects.css +21 -4
  169. package/src/styles/global.css +39 -3
  170. package/src/styles/utilities-layout.css +24 -1
  171. package/src/styles/utilities-navigation.css +6 -18
@@ -0,0 +1,390 @@
1
+ /* Pending Action approvals (`@k2b/cloud/approvals/ui`). A flat list: rows are set apart by space, not by lines or
2
+ boxes. A decided row keeps its content and height and turns its decision into a receipt in the same slot. */
3
+ .k2b-ui .cloud-approvals {
4
+ display: grid;
5
+ min-width: 0;
6
+ gap: 1rem;
7
+ }
8
+
9
+ .k2b-ui .cloud-approvals__toolbar {
10
+ display: flex;
11
+ min-height: 1.75rem;
12
+ align-items: center;
13
+ justify-content: space-between;
14
+ gap: 0.75rem;
15
+ color: var(--k2b-text-muted);
16
+ font-size: 0.75rem;
17
+ }
18
+
19
+ .k2b-ui .cloud-approvals__list {
20
+ display: grid;
21
+ min-width: 0;
22
+ gap: 2rem;
23
+ }
24
+
25
+ .k2b-ui .cloud-approvals__group {
26
+ display: grid;
27
+ min-width: 0;
28
+ gap: 1rem;
29
+ }
30
+
31
+ .k2b-ui .cloud-approvals__group + .cloud-approvals__group {
32
+ margin-top: 1rem;
33
+ }
34
+
35
+ .k2b-ui .cloud-approvals__group-title {
36
+ display: flex;
37
+ align-items: center;
38
+ gap: 0.5rem;
39
+ margin: 0;
40
+ color: var(--k2b-text);
41
+ font-size: 0.8125rem;
42
+ font-weight: 600;
43
+ }
44
+
45
+ .k2b-ui .cloud-approvals__more {
46
+ display: flex;
47
+ flex-wrap: wrap;
48
+ align-items: center;
49
+ justify-content: center;
50
+ gap: 0.5rem 0.75rem;
51
+ }
52
+
53
+ .k2b-ui .cloud-approvals__more-error {
54
+ display: inline-flex;
55
+ align-items: center;
56
+ gap: 0.375rem;
57
+ margin: 0;
58
+ color: var(--k2b-danger-text);
59
+ font-size: 0.75rem;
60
+ }
61
+
62
+ .k2b-ui .cloud-approvals__elsewhere {
63
+ display: flex;
64
+ flex-wrap: wrap;
65
+ align-items: center;
66
+ justify-content: space-between;
67
+ gap: 0.5rem 1rem;
68
+ margin: 0.5rem 0 0;
69
+ color: var(--k2b-text-muted);
70
+ font-size: 0.75rem;
71
+ }
72
+
73
+ .k2b-ui .cloud-approvals__elsewhere a {
74
+ display: inline-flex;
75
+ align-items: center;
76
+ gap: 0.25rem;
77
+ color: var(--k2b-link-accent);
78
+ font-weight: 500;
79
+ }
80
+
81
+ .k2b-ui .cloud-approval {
82
+ display: grid;
83
+ min-width: 0;
84
+ gap: 0.875rem;
85
+ grid-template-columns: 2rem minmax(0, 1fr);
86
+ }
87
+
88
+ .k2b-ui .cloud-approval__icon {
89
+ display: grid;
90
+ width: 2rem;
91
+ height: 2rem;
92
+ place-items: center;
93
+ border-radius: var(--k2b-radius-control);
94
+ color: var(--k2b-text-secondary);
95
+ background: var(--k2b-surface-muted);
96
+ font-size: 1rem;
97
+ }
98
+
99
+ /* Only an approval is a success; a rejection or a decision that did not apply keeps the neutral icon tile. */
100
+ .k2b-ui .cloud-approval[data-decided="approved"] .cloud-approval__icon {
101
+ color: var(--k2b-success-text);
102
+ background: var(--k2b-success-surface);
103
+ }
104
+
105
+ .k2b-ui .cloud-approval__main {
106
+ display: grid;
107
+ min-width: 0;
108
+ gap: 0.75rem;
109
+ container-type: inline-size;
110
+ }
111
+
112
+ .k2b-ui .cloud-approval__content {
113
+ display: grid;
114
+ min-width: 0;
115
+ gap: 0.5rem;
116
+ transition: opacity 150ms ease-out;
117
+ }
118
+
119
+ .k2b-ui .cloud-approval[data-decided] .cloud-approval__content {
120
+ opacity: 0.55;
121
+ }
122
+
123
+ .k2b-ui .cloud-approval__title,
124
+ .k2b-ui .cloud-approval__sheet-title {
125
+ margin: 0;
126
+ color: var(--k2b-text);
127
+ font-size: 0.875rem;
128
+ font-weight: 600;
129
+ line-height: 1.4;
130
+ overflow-wrap: anywhere;
131
+ }
132
+
133
+ .k2b-ui .cloud-approval__sheet-title {
134
+ font-size: 1.0625rem;
135
+ }
136
+
137
+ .k2b-ui .cloud-approval__origin {
138
+ margin: -0.25rem 0 0;
139
+ color: var(--k2b-text-muted);
140
+ font-size: 0.75rem;
141
+ overflow-wrap: anywhere;
142
+ }
143
+
144
+ .k2b-ui .cloud-approval__origin a {
145
+ color: inherit;
146
+ text-decoration: underline;
147
+ text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
148
+ text-underline-offset: 0.15em;
149
+ }
150
+
151
+ .k2b-ui .cloud-approval__origin a:hover {
152
+ color: var(--k2b-text);
153
+ }
154
+
155
+ .k2b-ui .cloud-approval__text {
156
+ margin: 0;
157
+ color: var(--k2b-text-secondary);
158
+ font-size: 0.8125rem;
159
+ white-space: pre-wrap;
160
+ overflow-wrap: anywhere;
161
+ }
162
+
163
+ .k2b-ui .cloud-approval__fields {
164
+ display: grid;
165
+ margin: 0;
166
+ gap: 0.25rem 1rem;
167
+ grid-template-columns: max-content minmax(0, 1fr);
168
+ font-size: 0.75rem;
169
+ }
170
+
171
+ .k2b-ui .cloud-approval__fields dt,
172
+ .k2b-ui .cloud-approval__label {
173
+ margin: 0;
174
+ color: var(--k2b-text-muted);
175
+ font-size: 0.75rem;
176
+ font-weight: 500;
177
+ }
178
+
179
+ .k2b-ui .cloud-approval__fields dd {
180
+ min-width: 0;
181
+ margin: 0;
182
+ color: var(--k2b-text);
183
+ white-space: pre-wrap;
184
+ overflow-wrap: anywhere;
185
+ }
186
+
187
+ .k2b-ui .cloud-approval__block {
188
+ display: grid;
189
+ min-width: 0;
190
+ gap: 0.25rem;
191
+ }
192
+
193
+ /* Long content sits inset, so it reads as quoted from the app without a frame or a rule. */
194
+ .k2b-ui .cloud-approval__quote,
195
+ .k2b-ui .cloud-approval__markdown {
196
+ margin: 0;
197
+ padding: 0.5rem 0.75rem;
198
+ border-radius: var(--k2b-radius-control);
199
+ color: var(--k2b-text-secondary);
200
+ background: var(--k2b-surface-muted);
201
+ font-size: 0.75rem;
202
+ line-height: 1.55;
203
+ white-space: pre-wrap;
204
+ overflow-wrap: anywhere;
205
+ }
206
+
207
+ /* The inner text clamps, so the quote's padding never shows a cut line. */
208
+ .k2b-ui .cloud-approval__quote[data-clamp] > span {
209
+ display: -webkit-box;
210
+ overflow: hidden;
211
+ -webkit-box-orient: vertical;
212
+ -webkit-line-clamp: 3;
213
+ }
214
+
215
+ .k2b-ui .cloud-approval__links {
216
+ display: flex;
217
+ flex-wrap: wrap;
218
+ gap: 0.25rem;
219
+ margin-left: -0.625rem;
220
+ }
221
+
222
+ .k2b-ui .cloud-approval__sheet {
223
+ display: grid;
224
+ min-width: 0;
225
+ gap: 1rem;
226
+ }
227
+
228
+ .k2b-ui .cloud-approval__sheet-foot {
229
+ width: 100%;
230
+ container-type: inline-size;
231
+ }
232
+
233
+ .k2b-ui .cloud-approval__callout {
234
+ display: flex;
235
+ align-items: flex-start;
236
+ gap: 0.5rem;
237
+ margin: 0;
238
+ color: var(--k2b-text-muted);
239
+ font-size: 0.75rem;
240
+ }
241
+
242
+ .k2b-ui .cloud-approval__callout i {
243
+ margin-top: 0.125rem;
244
+ }
245
+
246
+ /* The foot keeps its height in every state: the note is one line, and the decision slot keeps one button height, so a
247
+ receipt replaces the buttons without moving anything. A narrow row puts the decision on a line of its own. */
248
+ .k2b-ui .cloud-approval__foot {
249
+ display: grid;
250
+ min-width: 0;
251
+ align-items: center;
252
+ gap: 0.5rem 1rem;
253
+ grid-template-columns: minmax(0, 1fr) auto;
254
+ }
255
+
256
+ .k2b-ui .cloud-approval__note {
257
+ display: flex;
258
+ min-width: 0;
259
+ align-items: center;
260
+ gap: 0.375rem;
261
+ color: var(--k2b-text-muted);
262
+ font-size: 0.75rem;
263
+ white-space: nowrap;
264
+ }
265
+
266
+ .k2b-ui .cloud-approval__note > span {
267
+ min-width: 0;
268
+ overflow: hidden;
269
+ text-overflow: ellipsis;
270
+ }
271
+
272
+ .k2b-ui .cloud-approval__note[data-tone="danger"] {
273
+ color: var(--k2b-danger-text);
274
+ }
275
+
276
+ .k2b-ui .cloud-approval__actions {
277
+ display: flex;
278
+ min-height: 1.75rem;
279
+ min-width: 0;
280
+ align-items: center;
281
+ justify-content: flex-end;
282
+ gap: 0.375rem;
283
+ }
284
+
285
+ @container (max-width: 32rem) {
286
+ .k2b-ui .cloud-approval__foot {
287
+ grid-template-columns: minmax(0, 1fr);
288
+ }
289
+
290
+ /* The decision shares the width; a receipt keeps the same line. */
291
+ .k2b-ui .cloud-approval__actions > .k2b-button,
292
+ .k2b-ui .cloud-approval__actions > .k2b-split-button {
293
+ flex: 1;
294
+ min-width: 0;
295
+ justify-content: center;
296
+ }
297
+ }
298
+
299
+ .k2b-ui .cloud-approval__receipt {
300
+ display: inline-flex;
301
+ align-items: center;
302
+ gap: 0.375rem;
303
+ color: var(--k2b-text-muted);
304
+ font-size: 0.75rem;
305
+ font-weight: 500;
306
+ }
307
+
308
+ .k2b-ui .cloud-approval__receipt[data-outcome="approved"] {
309
+ color: var(--k2b-success-text);
310
+ }
311
+
312
+ /* The band sits above the resource a request would change, tinted like the approval card in a chat. */
313
+ .k2b-ui .cloud-approval-bands {
314
+ display: grid;
315
+ gap: 0.5rem;
316
+ }
317
+
318
+ /* Two rows in every width: the decision never shares a line with the title, so a receipt cannot rewrap it. */
319
+ .k2b-ui .cloud-approval-band {
320
+ display: grid;
321
+ min-width: 0;
322
+ align-items: center;
323
+ gap: 0.5rem 0.75rem;
324
+ grid-template-columns: auto minmax(0, 1fr);
325
+ container-type: inline-size;
326
+ padding: 0.75rem 1rem;
327
+ /* Transparent, so forced colours still draw an outline where the tint disappears. */
328
+ border: 1px solid transparent;
329
+ border-radius: var(--k2b-radius-surface);
330
+ color: var(--k2b-text-secondary);
331
+ background: color-mix(in srgb, var(--k2b-warning-surface) 70%, var(--k2b-surface-canvas));
332
+ font-size: 0.8125rem;
333
+ }
334
+
335
+ .k2b-ui.k2b-dark .cloud-approval-band,
336
+ .dark .k2b-ui .cloud-approval-band {
337
+ background: color-mix(in srgb, var(--k2b-warning-surface) 55%, var(--k2b-surface-canvas));
338
+ }
339
+
340
+ .k2b-ui .cloud-approval-band > i {
341
+ color: var(--k2b-warning-text);
342
+ font-size: 1.125rem;
343
+ }
344
+
345
+ .k2b-ui .cloud-approval-band[data-decided] {
346
+ background: var(--k2b-surface-muted);
347
+ }
348
+
349
+ .k2b-ui .cloud-approval-band[data-decided] > i {
350
+ color: var(--k2b-text-muted);
351
+ }
352
+
353
+ .k2b-ui .cloud-approval-band[data-decided="approved"] > i {
354
+ color: var(--k2b-success-text);
355
+ }
356
+
357
+ .k2b-ui .cloud-approval-band__text {
358
+ display: grid;
359
+ min-width: 0;
360
+ gap: 0.125rem;
361
+ }
362
+
363
+ .k2b-ui .cloud-approval-band__text strong {
364
+ color: var(--k2b-text);
365
+ font-weight: 600;
366
+ overflow-wrap: anywhere;
367
+ }
368
+
369
+ .k2b-ui .cloud-approval-band__text .cloud-approval__origin {
370
+ margin: 0;
371
+ }
372
+
373
+ .k2b-ui .cloud-approval-band > .cloud-approval__foot {
374
+ grid-column: 2;
375
+ }
376
+
377
+ @media (max-width: 40rem) {
378
+ .k2b-ui .cloud-approval {
379
+ gap: 0.75rem;
380
+ }
381
+
382
+ .k2b-ui .cloud-approval__fields {
383
+ grid-template-columns: minmax(0, 1fr);
384
+ gap: 0;
385
+ }
386
+
387
+ .k2b-ui .cloud-approval__fields dd {
388
+ margin-bottom: 0.35rem;
389
+ }
390
+ }
@@ -51,7 +51,7 @@
51
51
  }
52
52
  }
53
53
 
54
- /* Expanded work: one indent, one size, no lines or boxes; intermediate texts are quiet paragraphs. */
54
+ /* Expanded work: at the work line's left edge, one size, no lines or boxes; intermediate texts are quiet paragraphs. */
55
55
  .k2b-ui .ai-turn-steps {
56
56
  display: flex;
57
57
  min-width: 0;
@@ -60,20 +60,37 @@
60
60
 
61
61
  .k2b-ui .ai-turn-steps__text {
62
62
  padding-block: 0.25rem;
63
+ }
64
+
65
+ /* A live status in place 3 reads like those texts. It keeps the place's line height, so a status and the text that
66
+ replaces it line up, and the same text never grows when it turns quiet. */
67
+ .k2b-ui :is(.ai-turn-steps__text, .ai-turn__text--status) {
63
68
  color: var(--k2b-text-muted);
64
69
  }
65
70
 
66
- .k2b-ui .ai-turn-steps__text .assistant-markdown-block,
67
- .k2b-ui .ai-turn-steps__text .assistant-markdown-block :where(h1, h2, h3, h4, h5, h6) {
71
+ .k2b-ui :is(.ai-turn-steps__text, .ai-turn__text--status) .assistant-markdown-block,
72
+ .k2b-ui :is(.ai-turn-steps__text, .ai-turn__text--status) .assistant-markdown-block :where(h1, h2, h3, h4, h5, h6) {
68
73
  color: inherit;
69
74
  font-size: 0.75rem;
75
+ }
76
+
77
+ .k2b-ui .ai-turn-steps__text .assistant-markdown-block,
78
+ .k2b-ui .ai-turn-steps__text .assistant-markdown-block :where(h1, h2, h3, h4, h5, h6) {
70
79
  line-height: 1.55;
71
80
  }
72
81
 
73
- .k2b-ui .ai-turn-steps__text .assistant-markdown-block :where(p, ul, ol, pre, blockquote) {
82
+ .k2b-ui :is(.ai-turn-steps__text, .ai-turn__text--status) .assistant-markdown-block :where(p, ul, ol, pre, blockquote) {
74
83
  margin-block: 0.375rem;
75
84
  }
76
85
 
86
+ .k2b-ui .ai-turn__text--status .assistant-markdown-block > :where(:first-child) {
87
+ margin-top: 0;
88
+ }
89
+
90
+ .k2b-ui .ai-turn__text--status .assistant-markdown-block > :where(:last-child) {
91
+ margin-bottom: 0;
92
+ }
93
+
77
94
  .k2b-ui .ai-turn-steps__thought {
78
95
  margin: 0;
79
96
  color: var(--k2b-text-muted);
@@ -28,6 +28,7 @@
28
28
  @import "./utilities-markdown-editor.css";
29
29
  @import "./base-popover.css";
30
30
  @import "./effects.css";
31
+ @import "./approvals.css";
31
32
  @import "./input.css";
32
33
  @import "./resource-search.css";
33
34
  @import "./file-chooser.css";
@@ -123,7 +124,9 @@ html:has(.cloud-app-canvas[data-layout-full-page="true"]) {
123
124
  height: 100%;
124
125
  }
125
126
 
126
- :root .stdlib-chart {
127
+ /* Cloud's chart palette on every @k2b/ui surface, which sets its own palette on `.k2b-ui` (Cloud's body), so legends
128
+ and series filters, also in popovers, name the colors the chart draws. */
129
+ :root :is(.k2b-ui, .stdlib-chart, .k2b-chart-legend) {
127
130
  --stdlib-chart-c1: #14b8a6;
128
131
  --stdlib-chart-c2: #f97316;
129
132
  --stdlib-chart-c3: #8b5cf6;
@@ -281,8 +284,12 @@ solid-island {
281
284
  }
282
285
  }
283
286
 
284
- /* Cloud has exactly two shell modes: mobile below lg, desktop from lg. */
285
- @media (max-width: 1023px) {
287
+ /* Cloud has three shell modes. Phones, below md, get the header and no rail.
288
+ From md, signed-in people get the rail instead of the header: up to 2xl the
289
+ app area meets the window's top, right and bottom edges, and from 2xl it is
290
+ a card with the shell spacing. Anonymous pages keep the header and the card
291
+ from md. */
292
+ @media (max-width: 767px) {
286
293
  .cloud-app-canvas {
287
294
  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);
288
295
  }
@@ -309,6 +316,35 @@ solid-island {
309
316
 
310
317
  }
311
318
 
319
+ /* Flush: the app area is one opaque surface from the rail to the window's
320
+ edges, without padding, radius, shadow, or border. The rail keeps the app
321
+ canvas, so the change of surface alone separates the two; no line does. The
322
+ outermost workspace and the notices above it lose their frame, so nothing
323
+ draws a box inside the app area. */
324
+ @media (min-width: 768px) and (max-width: 1535px) {
325
+ .cloud-app-canvas[data-layout-authenticated] {
326
+ 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);
327
+ }
328
+ .cloud-app-canvas[data-layout-authenticated] > .layout-rail {
329
+ width: 3rem;
330
+ margin-inline-start: 0;
331
+ padding-bottom: var(--ui-space-shell);
332
+ }
333
+ .cloud-app-canvas[data-layout-authenticated] > .layout-shell-content {
334
+ gap: 0;
335
+ background: var(--k2b-surface);
336
+ }
337
+ .cloud-app-canvas[data-layout-authenticated] .layout-content-main .k2b-app-workspace:not(.k2b-app-workspace *) {
338
+ --k2b-workspace-radius: 0px;
339
+ border: 0;
340
+ box-shadow: none;
341
+ }
342
+ .cloud-app-canvas[data-layout-authenticated] > .layout-shell-content > :not(.layout-content-main) .k2b-notice-card {
343
+ border: 0;
344
+ border-radius: 0;
345
+ }
346
+ }
347
+
312
348
  .cloud-mobile-menu .k2b-panel-dialog__header {
313
349
  gap: .5rem;
314
350
  padding-inline: 1rem;
@@ -238,7 +238,7 @@
238
238
  }
239
239
 
240
240
  /* Shared shell frame and overview composition. */
241
- @media (min-width: 1024px) {
241
+ @media (min-width: 768px) {
242
242
  .cloud-app-canvas .layout-header {
243
243
  padding: 0.375rem 0.75rem 0.375rem 1.125rem;
244
244
  border-color: transparent;
@@ -601,3 +601,26 @@
601
601
  .dark .panel-dialog-tabs > button[aria-pressed="true"] {
602
602
  color: var(--color-zinc-50);
603
603
  }
604
+
605
+ /* The shell's row for the way up on pages without a workspace: the page title
606
+ with "‹ Parent" or the path above it, 46 px high like a workspace head and
607
+ without a line under it. It shows only on a page with a page above it; a
608
+ workspace's head or the page's own `PageTrail` takes its place. */
609
+ .layout-page-head {
610
+ display: none;
611
+ min-height: 2.875rem;
612
+ flex: none;
613
+ align-items: center;
614
+ padding: 0.25rem 0.5rem;
615
+ }
616
+
617
+ .layout-shell-content:not(:has(.layout-content-main .k2b-app-workspace, .k2b-page-title:not([data-fallback]))) .layout-page-head[data-trail="up"] {
618
+ display: flex;
619
+ }
620
+
621
+ /* After going up, the row that led down to where the person came from stays
622
+ marked, with the hover tint and nothing that moves. */
623
+ [data-page-trail-recent],
624
+ [data-page-trail-recent] > :is(td, th) {
625
+ background-color: var(--ui-hover);
626
+ }
@@ -829,18 +829,15 @@
829
829
  }
830
830
 
831
831
  /* Profile preferences stay Cloud-owned because their placement follows the
832
- global shell. Width, height, and pointer media queries select an already
833
- server-rendered variant; no client layout state or placeholder is needed. */
832
+ global shell: the avatar is the last control of the rail. Pointer media
833
+ queries select an already server-rendered variant; no client layout state
834
+ or placeholder is needed. */
834
835
  .layout-profile-preferences {
835
836
  position: relative;
836
837
  display: inline-flex;
837
838
  flex: none;
838
839
  }
839
840
 
840
- .layout-profile-preferences[data-placement="rail"] {
841
- display: none;
842
- }
843
-
844
841
  .layout-profile-preferences__link,
845
842
  .layout-profile-preferences__dropdown-trigger {
846
843
  display: none;
@@ -868,9 +865,12 @@
868
865
  box-shadow: var(--ui-focus);
869
866
  }
870
867
 
868
+ /* Beside the rail, growing upward from the avatar. */
871
869
  .layout-profile-preferences__panel {
872
870
  position: absolute;
873
871
  z-index: 100;
872
+ bottom: 0;
873
+ left: 100%;
874
874
  display: none;
875
875
  width: min(14rem, calc(100vw - 1rem));
876
876
  flex-direction: column;
@@ -887,16 +887,6 @@
887
887
  color: var(--color-zinc-300);
888
888
  }
889
889
 
890
- .layout-profile-preferences[data-placement="header"] .layout-profile-preferences__panel {
891
- top: 100%;
892
- right: 0;
893
- }
894
-
895
- .layout-profile-preferences[data-placement="rail"] .layout-profile-preferences__panel {
896
- bottom: 0;
897
- left: 100%;
898
- }
899
-
900
890
  .layout-profile-preferences:focus-within .layout-profile-preferences__panel {
901
891
  opacity: 1;
902
892
  visibility: visible;
@@ -923,9 +913,7 @@
923
913
  visibility: visible;
924
914
  pointer-events: auto;
925
915
  }
926
- }
927
916
 
928
- @media (min-width: 1024px) and (hover: hover) and (pointer: fine) {
929
917
  .layout-profile-preferences__link,
930
918
  .layout-profile-preferences__panel {
931
919
  display: flex;