@valentinkolb/cloud 0.4.0 → 0.5.1

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 (194) hide show
  1. package/package.json +18 -6
  2. package/scripts/preload.ts +78 -23
  3. package/src/_internal/define-app.ts +53 -46
  4. package/src/api/accounts-entities.ts +4 -0
  5. package/src/api/admin-core-settings.ts +98 -0
  6. package/src/api/announcements.ts +131 -0
  7. package/src/api/auth/schemas.ts +24 -0
  8. package/src/api/auth.ts +116 -13
  9. package/src/api/index.ts +7 -2
  10. package/src/api/me.ts +203 -14
  11. package/src/api/search/schemas.ts +1 -0
  12. package/src/api/search.ts +62 -8
  13. package/src/config/ssr.ts +2 -9
  14. package/src/contracts/announcements.test.ts +37 -0
  15. package/src/contracts/announcements.ts +121 -0
  16. package/src/contracts/app.ts +2 -0
  17. package/src/contracts/index.ts +3 -2
  18. package/src/contracts/registry.ts +2 -0
  19. package/src/contracts/shared.ts +108 -1
  20. package/src/desktop/index.ts +704 -0
  21. package/src/desktop/solid.tsx +938 -0
  22. package/src/server/api/index.ts +1 -1
  23. package/src/server/api/respond.ts +50 -10
  24. package/src/server/index.ts +44 -38
  25. package/src/server/middleware/auth.ts +98 -9
  26. package/src/server/middleware/index.ts +2 -1
  27. package/src/server/middleware/settings.ts +26 -0
  28. package/src/server/services/access.test.ts +197 -0
  29. package/src/server/services/access.ts +254 -6
  30. package/src/server/services/index.ts +14 -11
  31. package/src/server/services/pagination.ts +22 -0
  32. package/src/server/time.ts +45 -0
  33. package/src/services/account-lifecycle/index.ts +142 -18
  34. package/src/services/accounts/app.ts +658 -170
  35. package/src/services/accounts/authz.test.ts +77 -0
  36. package/src/services/accounts/authz.ts +22 -0
  37. package/src/services/accounts/entities.ts +84 -5
  38. package/src/services/accounts/groups.ts +30 -24
  39. package/src/services/accounts/model.test.ts +30 -0
  40. package/src/services/accounts/switching.test.ts +14 -0
  41. package/src/services/accounts/switching.ts +15 -6
  42. package/src/services/accounts/users.ts +75 -52
  43. package/src/services/announcements/index.test.ts +32 -0
  44. package/src/services/announcements/index.ts +224 -0
  45. package/src/services/audit/index.test.ts +84 -0
  46. package/src/services/audit/index.ts +431 -0
  47. package/src/services/auth-flows/index.ts +9 -2
  48. package/src/services/auth-flows/ipa.ts +47 -7
  49. package/src/services/auth-flows/magic-link.ts +92 -20
  50. package/src/services/auth-flows/password-reset.ts +284 -0
  51. package/src/services/auth-flows/proxy-return.test.ts +24 -0
  52. package/src/services/auth-flows/proxy-return.ts +49 -0
  53. package/src/services/gateway.ts +162 -0
  54. package/src/services/index.ts +44 -2
  55. package/src/services/ipa/effective-groups.test.ts +33 -0
  56. package/src/services/ipa/effective-groups.ts +70 -0
  57. package/src/services/ipa/profile.ts +45 -3
  58. package/src/services/ipa/search.ts +3 -5
  59. package/src/services/ipa/service-account.ts +15 -0
  60. package/src/services/ipa/sync-planning.test.ts +32 -0
  61. package/src/services/ipa/sync-planning.ts +22 -0
  62. package/src/services/ipa/sync.ts +110 -38
  63. package/src/services/notifications/index.ts +82 -11
  64. package/src/services/oauth-tokens.ts +104 -0
  65. package/src/services/postgres.ts +21 -6
  66. package/src/services/providers/local/auth.test.ts +22 -0
  67. package/src/services/providers/local/auth.ts +46 -3
  68. package/src/services/secrets.ts +10 -0
  69. package/src/services/service-account-credentials.test.ts +210 -0
  70. package/src/services/service-account-credentials.ts +715 -0
  71. package/src/services/service-accounts.ts +188 -0
  72. package/src/services/session/index.ts +7 -8
  73. package/src/services/settings/app.ts +4 -20
  74. package/src/services/settings/defaults.ts +79 -22
  75. package/src/services/settings/store.ts +47 -0
  76. package/src/services/weather/forecast.ts +40 -7
  77. package/src/services/webauthn.test.ts +36 -0
  78. package/src/services/webauthn.ts +384 -0
  79. package/src/shared/icons.ts +391 -100
  80. package/src/shared/index.ts +7 -0
  81. package/src/shared/markdown/extensions/code.ts +38 -1
  82. package/src/shared/markdown/extensions/images.ts +39 -3
  83. package/src/shared/markdown/extensions/info-blocks.ts +5 -5
  84. package/src/shared/markdown/extensions/mark.ts +48 -0
  85. package/src/shared/markdown/extensions/sub-sup.ts +60 -0
  86. package/src/shared/markdown/extensions/tables.ts +79 -58
  87. package/src/shared/markdown/formula.test.ts +1089 -0
  88. package/src/shared/markdown/formula.ts +1187 -0
  89. package/src/shared/markdown/index.ts +76 -2
  90. package/src/shared/mock-cover.ts +130 -0
  91. package/src/shared/redirect.test.ts +58 -0
  92. package/src/shared/redirect.ts +56 -0
  93. package/src/shared/theme.test.ts +24 -0
  94. package/src/shared/theme.ts +68 -0
  95. package/src/shared/time.ts +13 -0
  96. package/src/ssr/AdminLayout.tsx +7 -3
  97. package/src/ssr/AdminSidebar.tsx +115 -49
  98. package/src/ssr/AppLaunchpad.island.tsx +176 -0
  99. package/src/ssr/Footer.island.tsx +3 -8
  100. package/src/ssr/GlobalAnnouncements.island.tsx +141 -0
  101. package/src/ssr/GlobalSearchDialog.tsx +545 -117
  102. package/src/ssr/HotkeysHelpRail.island.tsx +3 -70
  103. package/src/ssr/Layout.tsx +74 -66
  104. package/src/ssr/LayoutBreadcrumbs.island.tsx +44 -0
  105. package/src/ssr/LayoutHelp.tsx +266 -0
  106. package/src/ssr/NavMenu.island.tsx +0 -39
  107. package/src/ssr/ThemeToggleRail.island.tsx +3 -3
  108. package/src/ssr/TimezoneCookie.island.tsx +23 -0
  109. package/src/ssr/islands/index.ts +13 -0
  110. package/src/styles/base-popover.css +5 -2
  111. package/src/styles/effects.css +87 -6
  112. package/src/styles/global.css +146 -9
  113. package/src/styles/input.css +3 -1
  114. package/src/styles/utilities-buttons.css +133 -27
  115. package/src/styles/utilities-code-display.css +67 -0
  116. package/src/styles/utilities-completion.css +223 -0
  117. package/src/styles/utilities-detail.css +73 -0
  118. package/src/styles/utilities-feedback.css +16 -15
  119. package/src/styles/utilities-layout.css +42 -2
  120. package/src/styles/utilities-markdown-editor.css +472 -0
  121. package/src/styles/utilities-navigation.css +63 -8
  122. package/src/styles/utilities-script.css +84 -0
  123. package/src/styles/utilities-table-tile.css +229 -0
  124. package/src/types/ambient.d.ts +9 -0
  125. package/src/ui/completion/behaviors.test.ts +95 -0
  126. package/src/ui/completion/behaviors.ts +205 -0
  127. package/src/ui/completion/engine.ts +368 -0
  128. package/src/ui/completion/index.ts +40 -0
  129. package/src/ui/completion/overlay.ts +92 -0
  130. package/src/ui/dialog-core.ts +173 -45
  131. package/src/ui/filter/FilterChip.tsx +42 -40
  132. package/src/ui/index.ts +11 -12
  133. package/src/ui/input/AutocompleteEditor.tsx +656 -0
  134. package/src/ui/input/CheckboxCard.tsx +91 -0
  135. package/src/ui/input/Combobox.tsx +375 -0
  136. package/src/ui/input/DatePicker.tsx +846 -0
  137. package/src/ui/input/DateTimeInput.tsx +29 -4
  138. package/src/ui/input/FileDropzone.tsx +116 -0
  139. package/src/ui/input/IconInput.tsx +116 -0
  140. package/src/ui/input/ImageInput.tsx +19 -2
  141. package/src/ui/input/MultiSelectInput.tsx +448 -0
  142. package/src/ui/input/NumberInput.tsx +417 -61
  143. package/src/ui/input/SegmentedControl.tsx +2 -2
  144. package/src/ui/input/Select.tsx +172 -10
  145. package/src/ui/input/Slider.tsx +3 -4
  146. package/src/ui/input/Switch.tsx +3 -2
  147. package/src/ui/input/TemplateEditor.tsx +212 -0
  148. package/src/ui/input/TextInput.tsx +144 -13
  149. package/src/ui/input/index.ts +53 -8
  150. package/src/ui/input/markdown/MarkdownEditor.tsx +774 -0
  151. package/src/ui/input/markdown/Toolbar.tsx +90 -0
  152. package/src/ui/input/markdown/actions.ts +233 -0
  153. package/src/ui/input/markdown/active-formats.ts +94 -0
  154. package/src/ui/input/markdown/behaviors.ts +193 -0
  155. package/src/ui/input/markdown/code-zone.ts +23 -0
  156. package/src/ui/input/markdown/highlight.ts +316 -0
  157. package/src/ui/layout.ts +22 -0
  158. package/src/ui/misc/AppOverview.tsx +105 -0
  159. package/src/ui/misc/AppWorkspace.tsx +607 -0
  160. package/src/ui/misc/Calendar.tsx +1291 -0
  161. package/src/ui/misc/Chart.tsx +162 -0
  162. package/src/ui/misc/CodeDisplay.tsx +54 -0
  163. package/src/ui/misc/ContextMenu.tsx +2 -2
  164. package/src/ui/misc/DataTable.tsx +269 -0
  165. package/src/ui/misc/DockWorkspace.tsx +425 -0
  166. package/src/ui/misc/Docs.tsx +153 -0
  167. package/src/ui/misc/Dropdown.tsx +2 -2
  168. package/src/ui/misc/EntitySearch.tsx +260 -129
  169. package/src/ui/misc/LinkCard.tsx +14 -2
  170. package/src/ui/misc/LogEntriesTable.tsx +34 -31
  171. package/src/ui/misc/Pagination.tsx +31 -12
  172. package/src/ui/misc/PanelDialog.tsx +109 -0
  173. package/src/ui/misc/Panes.tsx +873 -0
  174. package/src/ui/misc/PermissionEditor.tsx +358 -262
  175. package/src/ui/misc/Placeholder.tsx +40 -0
  176. package/src/ui/misc/ProgressBar.tsx +1 -1
  177. package/src/ui/misc/ResourceApiKeys.tsx +260 -0
  178. package/src/ui/misc/SettingsModal.tsx +150 -0
  179. package/src/ui/misc/StatCell.tsx +182 -40
  180. package/src/ui/misc/StatGrid.tsx +149 -0
  181. package/src/ui/misc/StructuredDataPreview.tsx +107 -0
  182. package/src/ui/misc/code-highlight.ts +213 -0
  183. package/src/ui/misc/index.ts +93 -12
  184. package/src/ui/prompts.tsx +362 -312
  185. package/src/ui/toast.ts +384 -0
  186. package/src/ui/widgets/Widget.tsx +12 -4
  187. package/src/ssr/MoreAppsDropdown.island.tsx +0 -61
  188. package/src/ui/ipa/GroupView.tsx +0 -36
  189. package/src/ui/ipa/LoginBtn.tsx +0 -16
  190. package/src/ui/ipa/UserView.tsx +0 -58
  191. package/src/ui/ipa/index.ts +0 -4
  192. package/src/ui/navigation.ts +0 -32
  193. package/src/ui/sidebar.tsx +0 -468
  194. /package/src/ui/{ipa → misc}/Avatar.tsx +0 -0
@@ -1,9 +1,17 @@
1
1
  /* Button and input utility classes — flat, solid */
2
+ @utility focus-ui {
3
+ @apply focus-visible:outline-none;
4
+
5
+ &:focus-visible {
6
+ box-shadow: var(--theme-focus-ring);
7
+ }
8
+ }
9
+
2
10
  @utility btn-base {
3
11
  @apply inline-flex cursor-pointer items-center justify-center gap-1.5 select-none rounded-md font-medium border;
4
- @apply transition-[background-color,color,border-color,opacity] duration-150 ease-out;
5
- @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-400 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-950;
6
- @apply disabled:cursor-not-allowed disabled:opacity-40 disabled:transition-none;
12
+ @apply transition-[background-color,color,border-color,box-shadow,opacity] duration-150 ease-out;
13
+ @apply focus-ui;
14
+ @apply disabled:cursor-not-allowed disabled:opacity-40 disabled:transition-none disabled:shadow-none;
7
15
  }
8
16
 
9
17
  @utility btn-sm {
@@ -27,17 +35,21 @@
27
35
  @apply dark:active:bg-zinc-700;
28
36
  }
29
37
 
30
- /* Primary: outlined blue, fills on hover */
38
+ /* Primary: filled blue + soft bevel; darkens on hover; sinks (inset) on press */
31
39
  @utility btn-primary {
32
- @apply btn-base bg-white text-blue-600 border-blue-400;
33
- @apply hover:bg-blue-500 hover:text-white hover:border-blue-500;
34
- @apply active:bg-blue-600 active:border-blue-600;
35
- @apply dark:bg-transparent dark:text-blue-400 dark:border-blue-400;
36
- @apply dark:hover:bg-blue-500 dark:hover:text-white dark:hover:border-blue-500;
37
- @apply dark:active:bg-blue-600;
40
+ @apply btn-base bg-blue-500 text-white border-blue-600;
41
+ @apply hover:bg-blue-600 hover:border-blue-700;
42
+ @apply active:bg-blue-700 active:border-blue-700;
43
+ @apply dark:bg-blue-500 dark:text-white dark:border-blue-600;
44
+ @apply dark:hover:bg-blue-600 dark:hover:border-blue-700;
45
+ @apply dark:active:bg-blue-700;
46
+ box-shadow: inset 0 1px 0 0 rgb(255 255 255 / 0.26), inset 0 -1px 1px 0 rgb(0 0 0 / 0.16);
47
+ &:active {
48
+ box-shadow: var(--theme-press);
49
+ }
38
50
  }
39
51
 
40
- /* Secondary: flat zinc, no border */
52
+ /* Secondary: filled zinc + soft bevel; sinks on press */
41
53
  @utility btn-secondary {
42
54
  @apply btn-base bg-zinc-100 text-zinc-700 border-transparent;
43
55
  @apply hover:bg-zinc-200;
@@ -45,40 +57,76 @@
45
57
  @apply dark:bg-zinc-800 dark:text-zinc-200 dark:border-transparent;
46
58
  @apply dark:hover:bg-zinc-700;
47
59
  @apply dark:active:bg-zinc-600;
60
+ box-shadow: var(--theme-bevel-top), var(--theme-bevel-bottom);
61
+ &:active {
62
+ box-shadow: var(--theme-press);
63
+ }
48
64
  }
49
65
 
50
- /* Success: outlined green, fills on hover */
66
+ /* Success: filled emerald + soft bevel; sinks on press */
51
67
  @utility btn-success {
52
- @apply btn-base bg-white text-emerald-600 border-emerald-400;
53
- @apply hover:bg-emerald-500 hover:text-white hover:border-emerald-500;
54
- @apply active:bg-emerald-600 active:border-emerald-600;
55
- @apply dark:bg-transparent dark:text-emerald-400 dark:border-emerald-400;
56
- @apply dark:hover:bg-emerald-500 dark:hover:text-white dark:hover:border-emerald-500;
57
- @apply dark:active:bg-emerald-600;
68
+ @apply btn-base bg-emerald-500 text-white border-emerald-600;
69
+ @apply hover:bg-emerald-600 hover:border-emerald-700;
70
+ @apply active:bg-emerald-700 active:border-emerald-700;
71
+ @apply dark:bg-emerald-500 dark:text-white dark:border-emerald-600;
72
+ @apply dark:hover:bg-emerald-600 dark:hover:border-emerald-700;
73
+ @apply dark:active:bg-emerald-700;
74
+ box-shadow: inset 0 1px 0 0 rgb(255 255 255 / 0.26), inset 0 -1px 1px 0 rgb(0 0 0 / 0.16);
75
+ &:active {
76
+ box-shadow: var(--theme-press);
77
+ }
58
78
  }
59
79
 
60
- /* Danger: outlined red, fills on hover */
80
+ /* Danger: filled red + soft bevel; sinks on press */
61
81
  @utility btn-danger {
62
- @apply btn-base bg-white text-red-600 border-red-400;
63
- @apply hover:bg-red-500 hover:text-white hover:border-red-500;
64
- @apply active:bg-red-600 active:border-red-600;
65
- @apply dark:bg-transparent dark:text-red-400 dark:border-red-400;
66
- @apply dark:hover:bg-red-500 dark:hover:text-white dark:hover:border-red-500;
67
- @apply dark:active:bg-red-600;
82
+ @apply btn-base bg-red-500 text-white border-red-600;
83
+ @apply hover:bg-red-600 hover:border-red-700;
84
+ @apply active:bg-red-700 active:border-red-700;
85
+ @apply dark:bg-red-500 dark:text-white dark:border-red-600;
86
+ @apply dark:hover:bg-red-600 dark:hover:border-red-700;
87
+ @apply dark:active:bg-red-700;
88
+ box-shadow: inset 0 1px 0 0 rgb(255 255 255 / 0.26), inset 0 -1px 1px 0 rgb(0 0 0 / 0.16);
89
+ &:active {
90
+ box-shadow: var(--theme-press);
91
+ }
68
92
  }
69
93
 
70
94
  @utility btn-input {
71
95
  @apply inline-flex items-center justify-center gap-1.5 rounded-md px-2.5 py-1 text-xs font-medium leading-5 select-none cursor-pointer;
72
- @apply transition-[background-color,color,border-color] duration-150 border;
96
+ @apply transition-[background-color,color,border-color,box-shadow] duration-150 border;
73
97
  @apply bg-zinc-100 text-zinc-600 border-zinc-100;
74
98
  @apply hover:bg-zinc-200/70 hover:text-zinc-800 hover:border-zinc-200/70;
75
99
  @apply active:bg-zinc-200;
76
100
  @apply focus-visible:outline-none focus-visible:bg-white focus-visible:border-blue-400;
101
+ @apply focus-ui;
77
102
  @apply dark:bg-zinc-800 dark:text-zinc-300 dark:border-zinc-800;
78
103
  @apply dark:hover:bg-zinc-700 dark:hover:text-zinc-100 dark:hover:border-zinc-700;
79
104
  @apply dark:active:bg-zinc-700;
80
105
  @apply dark:focus-visible:bg-zinc-900 dark:focus-visible:border-blue-400;
81
106
  @apply disabled:cursor-not-allowed disabled:opacity-40 disabled:transition-none;
107
+ box-shadow: var(--theme-bevel-top), var(--theme-bevel-bottom);
108
+ &:active {
109
+ box-shadow: var(--theme-press);
110
+ }
111
+ &:disabled {
112
+ box-shadow: none;
113
+ }
114
+ }
115
+
116
+ /* Recessed sibling of `btn-input`. Same filled surface + hover / focus /
117
+ disabled states, but sunk in (inset recess) instead of raised (bevel) — for
118
+ select / dropdown / picker triggers that should read as form fields rather
119
+ than press-buttons. Sinks deeper on :active. Compose with `btn-input-sm/-md`
120
+ for sizing, like `btn-input`. */
121
+ @utility btn-input-recessed {
122
+ @apply btn-input;
123
+ box-shadow: var(--theme-recess);
124
+ &:active {
125
+ box-shadow: var(--theme-press);
126
+ }
127
+ &:disabled {
128
+ box-shadow: none;
129
+ }
82
130
  }
83
131
 
84
132
  @utility btn-input-sm {
@@ -98,6 +146,44 @@
98
146
  @apply dark:hover:bg-blue-900 dark:hover:border-blue-700;
99
147
  }
100
148
 
149
+ @utility btn-segment {
150
+ @apply inline-flex cursor-pointer items-center justify-center gap-1 rounded-lg border border-zinc-300/50 bg-zinc-100/55 px-2.5 py-1 text-xs font-medium leading-4 text-zinc-700 select-none;
151
+ @apply transition-[background-color,color,border-color] duration-150 ease-out;
152
+ @apply hover:bg-zinc-50/65 hover:text-zinc-900;
153
+ @apply active:bg-zinc-200/80;
154
+ @apply focus-ui;
155
+ @apply dark:border-zinc-700/50 dark:bg-zinc-900/50 dark:text-zinc-500;
156
+ @apply dark:hover:bg-zinc-800/35 dark:hover:text-zinc-300;
157
+ @apply dark:active:bg-zinc-800/95;
158
+ @apply disabled:pointer-events-none disabled:opacity-50;
159
+ }
160
+
161
+ @utility btn-segment-icon {
162
+ @apply btn-segment h-7 w-7 px-0;
163
+ }
164
+
165
+ @utility btn-input-primary {
166
+ @apply btn-input bg-white text-blue-600 border-blue-400;
167
+ @apply hover:bg-blue-50 hover:text-blue-700 hover:border-blue-500;
168
+ @apply active:bg-blue-100 active:border-blue-500;
169
+ @apply focus-visible:bg-white focus-visible:border-blue-500;
170
+ @apply dark:bg-transparent dark:text-blue-400 dark:border-blue-400;
171
+ @apply dark:hover:bg-blue-950 dark:hover:text-blue-300 dark:hover:border-blue-400;
172
+ @apply dark:active:bg-blue-900;
173
+ @apply dark:focus-visible:bg-zinc-900 dark:focus-visible:border-blue-400;
174
+ }
175
+
176
+ @utility btn-input-success {
177
+ @apply btn-input bg-white text-emerald-600 border-emerald-400;
178
+ @apply hover:bg-emerald-50 hover:text-emerald-700 hover:border-emerald-500;
179
+ @apply active:bg-emerald-100 active:border-emerald-500;
180
+ @apply focus-visible:bg-white focus-visible:border-emerald-500;
181
+ @apply dark:bg-transparent dark:text-emerald-400 dark:border-emerald-400;
182
+ @apply dark:hover:bg-emerald-950 dark:hover:text-emerald-300 dark:hover:border-emerald-400;
183
+ @apply dark:active:bg-emerald-900;
184
+ @apply dark:focus-visible:bg-zinc-900 dark:focus-visible:border-emerald-400;
185
+ }
186
+
101
187
  /* Input: grey bg + matching border unfocused, white bg + blue border on focus */
102
188
  @utility input {
103
189
  padding: var(--theme-input-py) var(--theme-input-px);
@@ -110,6 +196,12 @@
110
196
  @apply dark:focus:!bg-zinc-900 dark:focus:!border-blue-400 dark:focus-within:!bg-zinc-900 dark:focus-within:!border-blue-400;
111
197
  @apply disabled:bg-zinc-50 disabled:text-zinc-400 disabled:border-zinc-50 disabled:cursor-not-allowed;
112
198
  @apply dark:disabled:bg-zinc-900 dark:disabled:text-zinc-500 dark:disabled:border-zinc-900;
199
+ @apply transition-[background-color,border-color,box-shadow] duration-150;
200
+ /* Recessed well (clip-safe). Stays recessed on focus while bg→white + blue border. */
201
+ box-shadow: var(--theme-recess);
202
+ &:disabled {
203
+ box-shadow: none;
204
+ }
113
205
  }
114
206
 
115
207
  @utility icon-btn {
@@ -119,7 +211,21 @@
119
211
  @apply bg-transparent border-0 shadow-none;
120
212
  @apply hover:text-zinc-700 hover:bg-zinc-100;
121
213
  @apply dark:hover:text-zinc-200 dark:hover:bg-zinc-800;
122
- @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-400 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-950;
214
+ @apply focus-ui;
123
215
  @apply active:bg-zinc-200 dark:active:bg-zinc-700;
124
216
  @apply disabled:cursor-not-allowed disabled:opacity-40 disabled:transition-none;
125
217
  }
218
+
219
+ /* Active / pressed state — set `aria-pressed="true"` on an `icon-btn`
220
+ to mark it as the currently-selected option (active filter, toggled
221
+ toolbar mode, etc.). Same blue tint as the markdown-editor toolbar's
222
+ active state so the platform reads consistently for "this is on". */
223
+ .icon-btn[aria-pressed="true"] {
224
+ @apply text-blue-600 bg-blue-50;
225
+ @apply dark:text-blue-400 dark:bg-blue-950/40;
226
+ }
227
+
228
+ .icon-btn[aria-pressed="true"]:hover {
229
+ @apply bg-blue-100 text-blue-700;
230
+ @apply dark:bg-blue-900/40 dark:text-blue-300;
231
+ }
@@ -0,0 +1,67 @@
1
+ /* Token classes emitted by `CodeDisplay`.
2
+ Small, dependency-free highlighting for examples and docs. */
3
+ .code-display-code,
4
+ .code-display-code * {
5
+ font-family: var(--font-mono);
6
+ }
7
+
8
+ .cd-c { @apply text-zinc-500 italic; }
9
+ .cd-s { @apply text-blue-900 dark:text-blue-200; }
10
+ .cd-n { @apply text-blue-700 dark:text-blue-300; }
11
+ .cd-k { @apply text-red-700 dark:text-red-400; }
12
+ .cd-p { @apply text-green-800 dark:text-green-300; }
13
+ .cd-a { @apply text-blue-700 dark:text-blue-300; }
14
+ .cd-f { @apply text-purple-700 dark:text-purple-300; }
15
+
16
+ .cd-md-syntax { @apply text-blue-700 dark:text-blue-300; }
17
+ .cd-md-checkbox { @apply text-blue-700 dark:text-blue-300; }
18
+ .cd-md-directive { @apply text-purple-700 dark:text-purple-300; }
19
+ .cd-md-ref { @apply text-blue-700 dark:text-blue-300; }
20
+ .cd-md-link { @apply text-blue-700 dark:text-blue-300; }
21
+ .cd-md-tag { @apply text-emerald-700 dark:text-emerald-300; }
22
+ .cd-md-code { @apply text-purple-700 dark:text-purple-300; }
23
+ .cd-md-formula { @apply text-amber-700 dark:text-amber-300; }
24
+
25
+ /* Shared in-product documentation blocks. `DocCode` accepts a custom
26
+ highlighter, usually from @valentinkolb/stdlib highlight.compile. */
27
+ .doc-code {
28
+ background: #f6f8fa;
29
+ border: 1px solid rgb(208 215 222 / 0.55);
30
+ box-shadow: 0 1px 2px rgb(31 35 40 / 0.04);
31
+ color: #24292f;
32
+ }
33
+
34
+ .dark .doc-code {
35
+ background: #0d1117;
36
+ border-color: rgb(48 54 61 / 0.7);
37
+ box-shadow: 0 1px 2px rgb(0 0 0 / 0.24);
38
+ color: #e6edf3;
39
+ }
40
+
41
+ .doc-code,
42
+ .doc-code * {
43
+ font-family: var(--font-mono);
44
+ }
45
+
46
+ .doc-token-comment { @apply text-zinc-500 italic; }
47
+ .doc-token-keyword { color: #0969da; font-weight: 700; }
48
+ .doc-token-aggregation,
49
+ .doc-token-function { color: #116329; font-weight: 700; }
50
+ .doc-token-duration,
51
+ .doc-token-number,
52
+ .doc-token-uuid { color: #8250df; }
53
+ .doc-token-string { color: #953800; }
54
+ .doc-token-placeholder { color: #64748b; font-style: italic; }
55
+ .doc-token-operator { color: #64748b; font-weight: 700; }
56
+ .doc-token-path,
57
+ .doc-token-identifier { color: inherit; }
58
+
59
+ .dark .doc-token-keyword { color: #79c0ff; }
60
+ .dark .doc-token-aggregation,
61
+ .dark .doc-token-function { color: #7ee787; }
62
+ .dark .doc-token-duration,
63
+ .dark .doc-token-number,
64
+ .dark .doc-token-uuid { color: #d2a8ff; }
65
+ .dark .doc-token-string { color: #ffa657; }
66
+ .dark .doc-token-placeholder { color: #94a3b8; }
67
+ .dark .doc-token-operator { color: #94a3b8; }
@@ -0,0 +1,223 @@
1
+ /* Generic completion overlay styles — used by both MarkdownEditor's
2
+ in-flow preview and the standalone AutocompleteEditor. Classes
3
+ are markdown-agnostic (`completion-*`, not `md-*`) so they apply
4
+ wherever the overlay pattern is used.
5
+
6
+ IMPORTANT: these styles sit inline with editor text in editors
7
+ that use the overtype (mirror-textarea) pattern, so glyph metrics
8
+ MUST be width-safe. The ghost-arrow span is allowed to deviate
9
+ (it's AFTER the caret, doesn't affect cursor alignment for typed
10
+ content). */
11
+
12
+ /* Ghost completion preview — zinc-400 tail at the cursor showing
13
+ the not-yet-typed continuation of the active suggestion. No
14
+ background — feels like a literal suggestion hovering in text,
15
+ not a separate UI element. */
16
+ .completion-ghost {
17
+ @apply text-zinc-400 dark:text-zinc-500;
18
+ }
19
+
20
+ /* Invisible caret anchor — injected at the cursor when a dropdown
21
+ is open but no ghost is shown (e.g. typed text equals the
22
+ highlighted row). Provides a measurable bounding rect for
23
+ `getBoundingClientRect()` without shifting any layout. */
24
+ .completion-caret-anchor {
25
+ display: inline-block;
26
+ width: 0;
27
+ height: 1em;
28
+ vertical-align: text-bottom;
29
+ pointer-events: none;
30
+ }
31
+
32
+ /* "Press Tab" arrow rendered after the ghost tail. Slightly larger
33
+ than body text so the hint is readable. Doesn't perturb cursor
34
+ alignment because it sits AFTER the caret position. */
35
+ .completion-ghost-arrow {
36
+ @apply text-zinc-400 dark:text-zinc-500;
37
+ margin-left: 0.4rem;
38
+ font-size: 1.15em;
39
+ line-height: 1;
40
+ }
41
+
42
+ /* ── AutocompleteEditor styles ─────────────────────────────────
43
+
44
+ Generic editor with optional syntax-highlighting overlay. When the
45
+ caller passes `highlight`, the textarea becomes invisible-text-only
46
+ and a preview layer beneath it renders the highlighted HTML
47
+ (overtype pattern). When no `highlight` is given, the textarea is
48
+ plain visible and the preview layer is omitted. */
49
+
50
+ .ac-editor {
51
+ position: relative;
52
+ @apply rounded-md border;
53
+ @apply bg-zinc-100 border-zinc-100;
54
+ @apply hover:bg-zinc-200/70 hover:border-zinc-200/70;
55
+ @apply dark:bg-zinc-800 dark:border-zinc-800;
56
+ @apply dark:hover:bg-zinc-700 dark:hover:border-zinc-700;
57
+ @apply transition-colors;
58
+ @apply overflow-hidden;
59
+ /* Recessed well — same inset as the `input` utility (this editor mirrors its
60
+ colours, so it gets its depth too). The recess persists on focus. */
61
+ box-shadow: var(--theme-recess);
62
+ }
63
+
64
+ .ac-editor:focus-within {
65
+ @apply bg-white border-blue-400;
66
+ @apply dark:bg-zinc-900 dark:border-blue-400;
67
+ }
68
+
69
+ .ac-editor[data-variant="paper"] {
70
+ @apply rounded-lg;
71
+ @apply bg-white border-zinc-100;
72
+ @apply hover:bg-white hover:border-zinc-100;
73
+ @apply dark:bg-zinc-900 dark:border-zinc-800;
74
+ @apply dark:hover:bg-zinc-900 dark:hover:border-zinc-800;
75
+ box-shadow: var(--theme-shadow-elevated);
76
+ }
77
+
78
+ .ac-editor[data-variant="paper"]:focus-within {
79
+ @apply bg-white border-blue-400;
80
+ @apply dark:bg-zinc-900 dark:border-blue-400;
81
+ }
82
+
83
+ .ac-editor[data-disabled="true"] {
84
+ @apply opacity-50 cursor-not-allowed;
85
+ }
86
+
87
+ .ac-editor[data-error="true"],
88
+ .ac-editor[data-error="true"]:focus-within {
89
+ @apply border-red-400 dark:border-red-500;
90
+ }
91
+
92
+ .ac-editor[data-fill="true"] {
93
+ height: 100%;
94
+ min-height: 0;
95
+ }
96
+
97
+ .ac-editor-surface {
98
+ position: relative;
99
+ height: var(--ac-h, 4.5rem);
100
+ }
101
+
102
+ .ac-editor-placeholder {
103
+ position: absolute;
104
+ inset: 0;
105
+ padding: 0.5rem 0.75rem;
106
+ pointer-events: none;
107
+ user-select: none;
108
+ @apply text-zinc-400 dark:text-zinc-500;
109
+ font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
110
+ font-size: 0.875rem;
111
+ line-height: 1.5;
112
+ z-index: 1;
113
+ }
114
+
115
+ .ac-editor-layer {
116
+ position: absolute !important;
117
+ inset: 0 !important;
118
+ width: 100% !important;
119
+ height: 100% !important;
120
+ margin: 0 !important;
121
+ border: 0 !important;
122
+ outline: 0 !important;
123
+ resize: none !important;
124
+ padding: 0.5rem 0.75rem !important;
125
+ font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important;
126
+ font-size: 0.875rem !important;
127
+ line-height: 1.5 !important;
128
+ font-weight: 400 !important;
129
+ font-style: normal !important;
130
+ letter-spacing: 0 !important;
131
+ white-space: pre-wrap !important;
132
+ word-wrap: break-word !important;
133
+ overflow-wrap: break-word !important;
134
+ background: transparent !important;
135
+ font-synthesis: none !important;
136
+ font-variant-ligatures: none !important;
137
+ }
138
+
139
+ .ac-editor-input {
140
+ @apply text-zinc-900 dark:text-zinc-100;
141
+ overflow-y: auto !important;
142
+ z-index: 2 !important;
143
+ /* Default visible textarea — caret-color inherits from text color */
144
+ }
145
+
146
+ /* Overlay mode: textarea text invisible, only caret + selection
147
+ show through. The preview underneath provides the visible glyphs. */
148
+ .ac-editor-input--overlay {
149
+ color: transparent !important;
150
+ -webkit-text-fill-color: transparent !important;
151
+ caret-color: #18181b !important;
152
+ background: transparent !important;
153
+ scrollbar-width: thin;
154
+ scrollbar-color: transparent transparent;
155
+ }
156
+
157
+ .dark .ac-editor-input--overlay {
158
+ caret-color: #f4f4f5 !important;
159
+ }
160
+
161
+ .ac-editor-input--overlay::selection {
162
+ background: rgb(59 130 246 / 0.35);
163
+ color: transparent;
164
+ }
165
+
166
+ .ac-editor-preview {
167
+ z-index: 1 !important;
168
+ pointer-events: none !important;
169
+ user-select: none !important;
170
+ overflow-y: scroll !important;
171
+ scrollbar-width: thin;
172
+ scrollbar-color: transparent transparent;
173
+ @apply text-zinc-900 dark:text-zinc-100;
174
+ }
175
+
176
+ /* Force every descendant (every span emitted by the user's
177
+ `highlight` callback) to inherit the layer's typography exactly.
178
+ Without this, a syntax-highlighter span can pick up a slightly
179
+ different font-family / font-size / letter-spacing from a Tailwind
180
+ reset, a UA default, or any cascading rule — accumulating into
181
+ a visible cursor-vs-glyph offset (the classic overtype drift).
182
+ `display: inline` + zero margins/padding/border guarantee no box
183
+ tweaks shift positions either. Same rule pattern as the
184
+ MarkdownEditor preview. */
185
+ .ac-editor-preview *,
186
+ .ac-editor-preview *::before,
187
+ .ac-editor-preview *::after {
188
+ font-family: inherit !important;
189
+ font-size: inherit !important;
190
+ line-height: inherit !important;
191
+ font-stretch: inherit !important;
192
+ letter-spacing: inherit !important;
193
+ word-spacing: inherit !important;
194
+ font-weight: inherit !important;
195
+ font-style: inherit !important;
196
+ margin: 0 !important;
197
+ padding: 0 !important;
198
+ border: 0 !important;
199
+ text-indent: 0 !important;
200
+ vertical-align: baseline !important;
201
+ display: inline !important;
202
+ }
203
+
204
+ .ac-editor-preview .fn {
205
+ @apply text-blue-600 dark:text-blue-300;
206
+ }
207
+ .ac-editor-preview .field {
208
+ @apply text-emerald-700 dark:text-emerald-300;
209
+ }
210
+ .ac-editor-preview .num {
211
+ @apply text-violet-700 dark:text-violet-300;
212
+ }
213
+ .ac-editor-preview .str {
214
+ @apply text-amber-700 dark:text-amber-300;
215
+ }
216
+ .ac-editor-preview .op {
217
+ @apply text-zinc-500 dark:text-zinc-400;
218
+ }
219
+
220
+ .ac-editor-preview::-webkit-scrollbar-thumb,
221
+ .ac-editor-input--overlay::-webkit-scrollbar-thumb {
222
+ background: transparent;
223
+ }
@@ -0,0 +1,73 @@
1
+ /* Detail panels — flow layout for info-dense detail surfaces.
2
+
3
+ Convention: the scrolling parent is `detail-stack`; every logical group is a
4
+ `detail-section`. The stack owns inter-section spacing, sections own their
5
+ inner padding and surface treatment. Empty sections must not render at all
6
+ (use Show / conditional rendering) so sparse records do not produce empty
7
+ shells. */
8
+
9
+ @utility detail-stack {
10
+ @apply flex h-full min-h-0 flex-1 flex-col gap-2 overflow-y-auto;
11
+ }
12
+
13
+ @utility detail-section {
14
+ /* Each section is its own surface card. The parent `detail-stack` owns
15
+ section-to-section spacing so callers do not mix margins and flex gaps. */
16
+ @apply paper p-4;
17
+ & > .detail-section-label + * {
18
+ @apply pt-0 mt-0;
19
+ }
20
+ }
21
+
22
+ @utility detail-section-compact {
23
+ @apply paper p-3;
24
+ & > .detail-section-label + * {
25
+ @apply pt-0 mt-0;
26
+ }
27
+ }
28
+
29
+ @utility detail-section-label {
30
+ /* Mini-header that anchors a section visually, e.g. "REACH", "WORK".
31
+ Stronger than `section-label` because there is no horizontal rule between
32
+ sections — the label has to do the delineation work on its own. */
33
+ @apply mb-3 text-xs font-semibold uppercase tracking-wider text-secondary select-none;
34
+ }
35
+
36
+ @utility detail-row {
37
+ /* Single-line row for emails, phones, websites, simple facts. Compose with
38
+ `detail-row-icon` on the leading icon and `detail-row-label` on the
39
+ optional small label slot. */
40
+ @apply flex items-baseline gap-1.5 py-1.5 text-xs text-primary;
41
+ }
42
+
43
+ @utility detail-row-icon {
44
+ /* Fixed-width icon slot for `detail-row` — keeps values aligned across rows.
45
+ Add a color utility (`text-blue-500`, `text-amber-500`, …) to colorize
46
+ per data type. */
47
+ @apply shrink-0 w-4 text-sm text-center self-center;
48
+ }
49
+
50
+ @utility detail-row-label {
51
+ /* Optional small label between icon and value, e.g. "work", "private". */
52
+ @apply shrink-0 w-24 truncate text-dimmed;
53
+ }
54
+
55
+ @utility detail-facts {
56
+ /* Compact key/value grid for facts without per-row icons (e.g. PERSONAL,
57
+ WORK groups). Children pair up: `detail-fact-key` then value. Each row is
58
+ separated by a hairline so the eye can follow key → value across the gap. */
59
+ @apply grid grid-cols-[8rem_1fr] gap-x-4 text-xs text-primary;
60
+ & > * {
61
+ @apply py-1.5 border-b border-zinc-100 dark:border-zinc-900;
62
+ }
63
+ & > *:nth-last-child(-n+2) {
64
+ @apply border-b-0 pb-0;
65
+ }
66
+ & > *:nth-child(-n+2) {
67
+ @apply pt-0;
68
+ }
69
+ }
70
+
71
+ @utility detail-fact-key {
72
+ @apply text-dimmed;
73
+ }
@@ -1,6 +1,15 @@
1
1
  /* Feedback blocks, badges, and chips — flat, solid, border-based */
2
2
  @utility info-block {
3
3
  @apply rounded-lg border px-3 py-2 text-sm leading-relaxed whitespace-pre-wrap;
4
+ box-shadow:
5
+ inset 0 1px 0 rgb(255 255 255 / 0.46),
6
+ inset 0 -1px 0 rgb(0 0 0 / 0.025);
7
+ }
8
+
9
+ .dark .info-block {
10
+ box-shadow:
11
+ inset 0 1px 0 rgb(255 255 255 / 0.045),
12
+ inset 0 -1px 0 rgb(0 0 0 / 0.28);
4
13
  }
5
14
 
6
15
  @utility info-block-note {
@@ -41,25 +50,17 @@
41
50
  }
42
51
 
43
52
  @utility popup {
44
- @apply rounded-lg bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 shadow-lg text-zinc-900 dark:text-zinc-100;
53
+ @apply rounded-lg bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 text-zinc-900 dark:text-zinc-100;
54
+ /* Floating layer (portaled) — outer shadow is fine here. */
55
+ box-shadow: var(--theme-shadow-float);
45
56
  }
46
57
 
47
58
  @utility tag {
48
59
  @apply rounded inline-flex items-center gap-1 px-1.5 py-0.5 text-[10px] font-medium;
49
60
  }
50
61
 
51
- /* Stat cards markup-only convention, see
62
+ /* Stat cards are component-owned by StatGrid/StatCell. See
52
63
  `skills/cloud-app/references/frontend.md` § Stats and the live demos in
53
- `packages/ui-lab/src/frontend/UiLabShowcase.island.tsx`. Pattern:
54
- <div class="paper overflow-hidden">
55
- <div class="grid grid-cols-N gap-px p-px bg-zinc-100 dark:bg-zinc-800">
56
- <div class="bg-white dark:bg-zinc-900 px-4 py-4 flex flex-col gap-0.5">
57
- <span class="text-[10px] uppercase tracking-wider text-dimmed">Label</span>
58
- <span class="text-xl font-bold tabular-nums text-primary">Value</span>
59
- <span class="text-[10px] text-dimmed">Sub</span>
60
- </div>
61
- </div>
62
- </div>
63
- The earlier `.stat-grid / .stat-cell / .stat-value / .stat-label / .stat-sub`
64
- utilities used `divide-x` + centered cells — DO NOT reintroduce. They
65
- produced double borders at corners and missing borders on edges. */
64
+ `packages/ui-lab/src/frontend/lab/surfaces-cards.tsx`. Do not reintroduce
65
+ the earlier `.stat-grid / .stat-cell / .stat-value / .stat-label / .stat-sub`
66
+ utilities; app code should import the shared components instead. */