@valentinkolb/cloud 0.3.1 → 0.5.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 (194) hide show
  1. package/package.json +18 -8
  2. package/scripts/preload.ts +78 -23
  3. package/src/_internal/define-app.ts +119 -47
  4. package/src/_internal/runtime-context.ts +1 -0
  5. package/src/api/accounts-entities.ts +4 -0
  6. package/src/api/admin-core-settings.ts +98 -0
  7. package/src/api/announcements.ts +131 -0
  8. package/src/api/auth/schemas.ts +24 -0
  9. package/src/api/auth.ts +113 -10
  10. package/src/api/index.ts +15 -25
  11. package/src/api/me.ts +203 -14
  12. package/src/api/search/schemas.ts +1 -0
  13. package/src/api/search.ts +62 -8
  14. package/src/config/ssr.ts +2 -9
  15. package/src/contracts/announcements.test.ts +37 -0
  16. package/src/contracts/announcements.ts +121 -0
  17. package/src/contracts/app.ts +4 -0
  18. package/src/contracts/index.ts +3 -2
  19. package/src/contracts/registry.ts +4 -0
  20. package/src/contracts/shared.ts +108 -1
  21. package/src/desktop/index.ts +704 -0
  22. package/src/desktop/solid.tsx +938 -0
  23. package/src/server/api/index.ts +1 -1
  24. package/src/server/api/respond.ts +50 -10
  25. package/src/server/index.ts +44 -38
  26. package/src/server/middleware/auth.ts +98 -9
  27. package/src/server/middleware/index.ts +2 -1
  28. package/src/server/middleware/settings.ts +26 -0
  29. package/src/server/services/access.test.ts +197 -0
  30. package/src/server/services/access.ts +254 -6
  31. package/src/server/services/index.ts +14 -11
  32. package/src/server/services/pagination.ts +22 -0
  33. package/src/server/time.ts +45 -0
  34. package/src/services/account-lifecycle/index.ts +142 -18
  35. package/src/services/accounts/app.ts +658 -170
  36. package/src/services/accounts/authz.test.ts +77 -0
  37. package/src/services/accounts/authz.ts +22 -0
  38. package/src/services/accounts/entities.ts +84 -5
  39. package/src/services/accounts/groups.ts +30 -24
  40. package/src/services/accounts/model.test.ts +30 -0
  41. package/src/services/accounts/switching.test.ts +14 -0
  42. package/src/services/accounts/switching.ts +15 -6
  43. package/src/services/accounts/users.ts +75 -52
  44. package/src/services/announcements/index.test.ts +32 -0
  45. package/src/services/announcements/index.ts +224 -0
  46. package/src/services/audit/index.test.ts +84 -0
  47. package/src/services/audit/index.ts +431 -0
  48. package/src/services/auth-flows/index.ts +9 -2
  49. package/src/services/auth-flows/ipa.ts +0 -2
  50. package/src/services/auth-flows/magic-link.ts +3 -2
  51. package/src/services/auth-flows/password-reset.ts +284 -0
  52. package/src/services/auth-flows/proxy-return.test.ts +24 -0
  53. package/src/services/auth-flows/proxy-return.ts +49 -0
  54. package/src/services/gateway.ts +162 -0
  55. package/src/services/index.ts +44 -2
  56. package/src/services/ipa/effective-groups.test.ts +33 -0
  57. package/src/services/ipa/effective-groups.ts +70 -0
  58. package/src/services/ipa/profile.ts +45 -3
  59. package/src/services/ipa/search.ts +3 -5
  60. package/src/services/ipa/service-account.ts +15 -0
  61. package/src/services/ipa/sync-planning.test.ts +32 -0
  62. package/src/services/ipa/sync-planning.ts +22 -0
  63. package/src/services/ipa/sync.ts +110 -38
  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 +64 -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 +49 -0
  92. package/src/shared/redirect.ts +52 -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,12 +1,6 @@
1
1
  import type { Role } from "../contracts/shared";
2
2
  import { Dropdown } from "../ui";
3
3
 
4
- type MobileAppLink = {
5
- href: string;
6
- iconClass: string;
7
- label: string;
8
- };
9
-
10
4
  /**
11
5
  * Minimal user projection for the nav menu — covers exactly what's rendered
12
6
  * (initials, display name, uid, profile flag, admin role check). Avoids
@@ -22,11 +16,8 @@ export type NavMenuUser = {
22
16
 
23
17
  type NavMenuProps = {
24
18
  user?: NavMenuUser;
25
- mobileApps: MobileAppLink[];
26
19
  };
27
20
 
28
- const hasRole = (roles: Role[], ...required: Role[]) => required.some((role) => roles.includes(role));
29
-
30
21
  /** Navigation dropdown menu - always visible, adapts to auth state. */
31
22
  export default function NavMenu(props: NavMenuProps) {
32
23
  const getElements = () => [
@@ -61,36 +52,6 @@ export default function NavMenu(props: NavMenuProps) {
61
52
  href: "/auth/login",
62
53
  },
63
54
  ]),
64
- // Section: Apps (mobile only — on desktop, tabs/rail handle this)
65
- ...(props.user
66
- ? [
67
- {
68
- element: (
69
- <div class="md:hidden">
70
- <div class="px-4 pt-3 pb-1 text-xs uppercase tracking-wider font-medium text-zinc-500">Apps</div>
71
- {props.mobileApps.map((app) => (
72
- <a
73
- href={app.href}
74
- class="flex w-full items-center gap-3 px-4 py-2 text-sm transition-colors hover:bg-white/30 dark:hover:bg-white/10 text-zinc-700 dark:text-zinc-300"
75
- >
76
- <i class={app.iconClass} />
77
- <span>{app.label}</span>
78
- </a>
79
- ))}
80
- {hasRole(props.user.roles, "admin") && (
81
- <a
82
- href="/admin"
83
- class="flex w-full items-center gap-3 px-4 py-2 text-sm transition-colors hover:bg-white/30 dark:hover:bg-white/10 text-zinc-700 dark:text-zinc-300"
84
- >
85
- <i class="ti ti-shield-cog" />
86
- <span>Admin</span>
87
- </a>
88
- )}
89
- </div>
90
- ),
91
- },
92
- ]
93
- : []),
94
55
  ];
95
56
 
96
57
  return (
@@ -1,12 +1,12 @@
1
1
  import { createSignal } from "solid-js";
2
- import { theme, type ThemeMode } from "@valentinkolb/stdlib/browser";
2
+ import { getCurrentThemePreference, setThemePreference, type CloudTheme } from "../shared/theme";
3
3
 
4
4
  /** Theme toggle button for the desktop rail navigation. */
5
5
  export default function ThemeToggleRail() {
6
- const [mode, setMode] = createSignal<ThemeMode>(typeof document !== "undefined" ? theme.getCurrent() : "light");
6
+ const [mode, setMode] = createSignal<CloudTheme>(getCurrentThemePreference());
7
7
 
8
8
  const toggleTheme = () => {
9
- setMode(theme.toggle());
9
+ setMode(setThemePreference(mode() === "dark" ? "light" : "dark"));
10
10
  };
11
11
 
12
12
  return (
@@ -0,0 +1,23 @@
1
+ import { cookies } from "@valentinkolb/stdlib/browser";
2
+ import { onMount } from "solid-js";
3
+ import { TIMEZONE_COOKIE } from "../shared/time";
4
+
5
+ export default function TimezoneCookie() {
6
+ onMount(() => {
7
+ const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
8
+ const current = cookies.readCookie(TIMEZONE_COOKIE);
9
+ if (timeZone && current !== timeZone) {
10
+ cookies.writeCookie(TIMEZONE_COOKIE, timeZone);
11
+ try {
12
+ if (sessionStorage.getItem("cloud.timezone.reload") !== timeZone) {
13
+ sessionStorage.setItem("cloud.timezone.reload", timeZone);
14
+ window.location.reload();
15
+ }
16
+ } catch {
17
+ // Cookie persistence still succeeds; the next navigation will render with the browser timezone.
18
+ }
19
+ }
20
+ });
21
+
22
+ return null;
23
+ }
@@ -1 +1,14 @@
1
1
  export { default as SearchBar } from "./SearchBar.island";
2
+ import { LayoutHelp } from "../LayoutHelp";
3
+ import { AppLaunchpadButton, AppLaunchpadProvider } from "../AppLaunchpad.island";
4
+
5
+ export { AppLaunchpadButton, AppLaunchpadProvider, openAppLaunchpad, setAppLaunchpadContext } from "../AppLaunchpad.island";
6
+ export type { AppLaunchpadApp, AppLaunchpadLegalLink } from "../AppLaunchpad.island";
7
+ export { LayoutHelp, openLayoutHelpDialog } from "../LayoutHelp";
8
+ export type { LayoutHelpProps, LayoutHelpTab } from "../LayoutHelp";
9
+
10
+ export const Layout = {
11
+ Help: LayoutHelp,
12
+ AppLaunchpadButton,
13
+ AppLaunchpadProvider,
14
+ };
@@ -4,8 +4,11 @@ solid-island {
4
4
  display: contents;
5
5
  }
6
6
 
7
- /* Popover positioning */
8
- [popover]:not(.paper) {
7
+ /* Popover positioning. Strip the UA-default white box for popovers
8
+ that don't opt into one of our paper-ish surfaces (`.paper`,
9
+ `.popup`) — those are typically tooltips / soft-hint popovers that
10
+ provide their own chrome. */
11
+ [popover]:not(.paper):not(.popup) {
9
12
  background: transparent;
10
13
  border: none;
11
14
  }
@@ -45,12 +45,7 @@
45
45
  content: "";
46
46
  position: absolute;
47
47
  inset: -50%;
48
- background: linear-gradient(
49
- 115deg,
50
- transparent 0%,
51
- rgba(255, 255, 255, 0.25) 50%,
52
- transparent 100%
53
- );
48
+ background: linear-gradient(115deg, transparent 0%, rgba(255, 255, 255, 0.25) 50%, transparent 100%);
54
49
  animation: shimmer 5s ease-in-out infinite;
55
50
  }
56
51
  }
@@ -63,3 +58,89 @@
63
58
  transform: translateX(100%);
64
59
  }
65
60
  }
61
+
62
+ /* Dashboard launchpad panel. Scoped through :has() so the calmer backdrop only
63
+ applies to the dashboard app picker, not every bare dialog. */
64
+ dialog:has(.launchpad-panel)::backdrop {
65
+ background: rgb(0 0 0 / 0.45);
66
+ backdrop-filter: blur(8px);
67
+ }
68
+
69
+ .dark dialog:has(.launchpad-panel)::backdrop {
70
+ background: rgb(0 0 0 / 0.35);
71
+ }
72
+
73
+ .launchpad-panel {
74
+ background: rgb(255 255 255 / 0.96);
75
+ border: 1px solid rgb(228 228 231 / 0.82);
76
+ border-radius: 1.5rem;
77
+ box-shadow:
78
+ inset 0 1px 0 0 rgb(255 255 255 / 0.9),
79
+ 0 28px 68px -42px rgb(24 24 27 / 0.48);
80
+ }
81
+
82
+ .dark .launchpad-panel {
83
+ background: linear-gradient(180deg, rgb(24 24 27 / 0.98), rgb(15 15 17 / 0.98));
84
+ border-color: rgb(255 255 255 / 0.13);
85
+ box-shadow:
86
+ inset 0 1px 0 0 rgb(255 255 255 / 0.08),
87
+ 0 28px 68px -36px rgb(0 0 0 / 0.82);
88
+ }
89
+
90
+ /* Dashboard launchpad app icon. One app colour drives the glyph and graded rim;
91
+ the face stays clean so the tile feels crisp instead of plastic. */
92
+ .launchpad-panel .app-icon {
93
+ --icon-face: #ffffff;
94
+ --rim-top: color-mix(in oklab, var(--app-icon-color), white 34%);
95
+ --rim-bottom: color-mix(in oklab, var(--app-icon-color), black 12%);
96
+ --rim-top-hover: color-mix(in oklab, var(--app-icon-color), white 2%);
97
+ --rim-bottom-hover: color-mix(in oklab, var(--app-icon-color), black 30%);
98
+ --glyph: var(--app-icon-color);
99
+ --glyph-hover: var(--app-icon-color);
100
+ --drop: none;
101
+
102
+ background:
103
+ linear-gradient(var(--icon-face), var(--icon-face)) padding-box,
104
+ linear-gradient(180deg, var(--rim-top), var(--rim-bottom)) border-box;
105
+ border: 2.5px solid transparent;
106
+ box-shadow: var(--drop);
107
+ color: var(--glyph);
108
+ transition:
109
+ background 0.18s ease,
110
+ box-shadow 0.18s ease,
111
+ color 0.18s ease;
112
+ }
113
+
114
+ @supports (color: oklch(from red l c h)) {
115
+ .launchpad-panel .app-icon {
116
+ --glyph: oklch(from var(--app-icon-color) min(l, 0.5) c h);
117
+ --glyph-hover: oklch(from var(--app-icon-color) min(l, 0.4) c h);
118
+ }
119
+ }
120
+
121
+ .dark .launchpad-panel .app-icon {
122
+ --icon-face: #202026;
123
+ --rim-top: color-mix(in oklab, var(--app-icon-color), white 30%);
124
+ --rim-bottom: color-mix(in oklab, var(--app-icon-color), black 24%);
125
+ --rim-top-hover: color-mix(in oklab, var(--app-icon-color), white 54%);
126
+ --rim-bottom-hover: color-mix(in oklab, var(--app-icon-color), white 6%);
127
+ --glyph: var(--app-icon-color);
128
+ --glyph-hover: color-mix(in oklab, var(--app-icon-color), white 32%);
129
+ --drop: 0 10px 20px -12px rgb(0 0 0 / 0.7);
130
+ }
131
+
132
+ @supports (color: oklch(from red l c h)) {
133
+ .dark .launchpad-panel .app-icon {
134
+ --glyph: oklch(from var(--app-icon-color) max(l, 0.82) c h);
135
+ --glyph-hover: oklch(from var(--app-icon-color) max(l, 0.96) c h);
136
+ }
137
+ }
138
+
139
+ /* Hover changes only the icon tile, never the whole icon+label item. */
140
+ .launchpad-panel .group:hover > .app-icon,
141
+ .launchpad-panel .app-icon:hover {
142
+ background:
143
+ linear-gradient(var(--icon-face), var(--icon-face)) padding-box,
144
+ linear-gradient(180deg, var(--rim-top-hover), var(--rim-bottom-hover)) border-box;
145
+ color: var(--glyph-hover);
146
+ }
@@ -10,12 +10,36 @@
10
10
  @custom-variant light (html:not(.dark) &);
11
11
 
12
12
  @import "@tabler/icons-webfont/dist/tabler-icons.css";
13
- @import "@fontsource-variable/jetbrains-mono";
13
+
14
+ /* IBM Plex family — Sans for body, Mono for code, Condensed for badges/tags.
15
+ Per-weight imports keep the network payload predictable; we deliberately
16
+ skip the lighter weights (100–300) and italic-condensed since the design
17
+ system never uses them. */
18
+ @import "@fontsource/ibm-plex-sans/400.css";
19
+ @import "@fontsource/ibm-plex-sans/400-italic.css";
20
+ @import "@fontsource/ibm-plex-sans/500.css";
21
+ @import "@fontsource/ibm-plex-sans/600.css";
22
+ @import "@fontsource/ibm-plex-sans/700.css";
23
+ @import "@fontsource/ibm-plex-mono/400.css";
24
+ @import "@fontsource/ibm-plex-mono/400-italic.css";
25
+ @import "@fontsource/ibm-plex-mono/500.css";
26
+ @import "@fontsource/ibm-plex-mono/600.css";
27
+ @import "@fontsource/ibm-plex-sans-condensed/400.css";
28
+ @import "@fontsource/ibm-plex-sans-condensed/500.css";
29
+ @import "@fontsource/ibm-plex-sans-condensed/600.css";
30
+ @import "@fontsource/ibm-plex-sans-condensed/700.css";
31
+
14
32
  @import "./tokens.css";
15
33
  @import "./utilities-buttons.css";
16
34
  @import "./utilities-layout.css";
17
35
  @import "./utilities-navigation.css";
18
36
  @import "./utilities-feedback.css";
37
+ @import "./utilities-detail.css";
38
+ @import "./utilities-table-tile.css";
39
+ @import "./utilities-script.css";
40
+ @import "./utilities-markdown-editor.css";
41
+ @import "./utilities-completion.css";
42
+ @import "./utilities-code-display.css";
19
43
  @import "./base-popover.css";
20
44
  @import "./effects.css";
21
45
  @import "./input.css";
@@ -24,11 +48,34 @@
24
48
  --color-dark: rgb(9 9 11);
25
49
  --color-selection-bg: rgb(147 197 253 / 0.45);
26
50
  --color-selection-fg: rgb(15 23 42);
51
+
52
+ /* Type system — IBM Plex family.
53
+ - Sans: default body / UI text (readable, neutral, slight personality)
54
+ - Mono: code, IDs, technical values (matches sans proportions)
55
+ - Condensed: badges, tags, dense labels where Sans is too wide
56
+
57
+ Tailwind 4 derives `font-sans` / `font-mono` / `font-condensed`
58
+ utilities from these tokens. CSS consumers can also reference them
59
+ via `var(--font-sans)` etc. (e.g. CodeMirror theme overrides). */
60
+ --font-sans: "IBM Plex Sans", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
61
+ --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
62
+ --font-condensed: "IBM Plex Sans Condensed", "IBM Plex Sans", system-ui, sans-serif;
27
63
  }
28
64
 
29
65
  :root {
30
- --theme-shadow-elevated: none;
31
- --theme-shadow-hover: none;
66
+ color-scheme: light;
67
+ /* Depth language (visual redesign). Inset bevel for in-flow surfaces (clip-safe),
68
+ recess for input wells, press for :active, float for PORTALED overlays only.
69
+ `--theme-shadow-elevated` flows the bevel through the token that `paper` and
70
+ the Layout header already consume, so they stay consistent. */
71
+ --theme-bevel-top: inset 0 1px 0 0 rgb(255 255 255 / 0.85);
72
+ --theme-bevel-bottom: inset 0 -1px 1px 0 rgb(0 0 0 / 0.05);
73
+ --theme-recess: inset 0 1px 2px rgb(0 0 0 / 0.08);
74
+ --theme-recess-sm: inset 0 1px 1px rgb(0 0 0 / 0.08);
75
+ --theme-press: inset 0 2px 4px rgb(0 0 0 / 0.19), inset 0 1px 1px rgb(0 0 0 / 0.1);
76
+ --theme-shadow-float: 0 12px 32px -10px rgb(0 0 0 / 0.22), 0 4px 10px -4px rgb(0 0 0 / 0.1);
77
+ --theme-shadow-elevated: var(--theme-bevel-top), var(--theme-bevel-bottom);
78
+ --theme-shadow-hover: var(--theme-bevel-top), var(--theme-bevel-bottom), 0 6px 16px -6px rgb(0 0 0 / 0.14);
32
79
  --theme-input-py: 0.375rem;
33
80
  --theme-input-px: 0.5rem;
34
81
  --theme-list-active-border: none;
@@ -43,11 +90,12 @@
43
90
  --theme-rail-item-active-border-r: none;
44
91
  --theme-rail-item-inactive-border-r: none;
45
92
  --theme-divider: 1px solid rgb(228 228 231);
93
+ --theme-focus-ring: inset 0 0 0 2px color-mix(in srgb, var(--color-blue-500) 45%, transparent);
46
94
  }
47
95
 
48
96
  * {
49
97
  font-variant-ligatures: contextual;
50
- font-family: "JetBrains Mono Variable", monospace;
98
+ font-family: var(--font-sans);
51
99
  scrollbar-width: thin;
52
100
  scrollbar-color: rgb(161 161 170 / 0.4) transparent;
53
101
  }
@@ -56,6 +104,84 @@
56
104
  scrollbar-color: rgb(82 82 91 / 0.5) transparent;
57
105
  }
58
106
 
107
+ .dark {
108
+ --theme-focus-ring: inset 0 0 0 2px color-mix(in srgb, var(--color-blue-400) 55%, transparent);
109
+ }
110
+
111
+ /* stdlib charts: make the SVG fill its wrapping element.
112
+ stdlib emits `<svg viewBox="0 0 W H">` without `width`/`height`
113
+ attributes, which would otherwise fall back to the browser's
114
+ replaced-element default (300×150) and either overflow tiny
115
+ containers or sit dwarfed inside wider ones. `display: block`
116
+ stops the inline-baseline gap; `width/height: 100%` lets the
117
+ container's CSS box drive the SVG size, and the `<Chart>`
118
+ wrapper passes the measured pixel dimensions back to stdlib so
119
+ the viewBox matches — no aspect distortion, no letterboxing. */
120
+ .stdlib-chart {
121
+ display: block;
122
+ width: 100%;
123
+ height: 100%;
124
+ }
125
+
126
+ :root .stdlib-chart {
127
+ --stdlib-chart-c1: #14b8a6;
128
+ --stdlib-chart-c2: #f97316;
129
+ --stdlib-chart-c3: #8b5cf6;
130
+ --stdlib-chart-c4: #ec4899;
131
+ --stdlib-chart-c5: #22c55e;
132
+ --stdlib-chart-c6: #eab308;
133
+ --stdlib-chart-c7: #06b6d4;
134
+ --stdlib-chart-c8: #ef4444;
135
+ }
136
+
137
+ /* stdlib charts — dark-mode overrides.
138
+
139
+ stdlib's embedded <style> hardcodes a light-mode `color: #1f2937`
140
+ on .stdlib-chart and `stroke: white` on slice/point/outlier
141
+ separators. The hardcoded color leaves axis labels / tick text
142
+ unreadable on a dark background, and white slice strokes look
143
+ harsh against zinc-950. We override BOTH from outside the SVG
144
+ with higher specificity (parent `.dark` selector beats the
145
+ embedded one-class rule).
146
+
147
+ Why specificity matters: stdlib's <style> lives INSIDE the SVG,
148
+ so its rules are part of the document. Both rule sets have one
149
+ class selector each (`.stdlib-chart`), and the embedded one is
150
+ parsed AFTER global.css — without raising specificity here, the
151
+ embedded rule would win and dark mode would stay broken. The
152
+ `.dark` parent on `<html>` (set by the platform's theme toggle)
153
+ bumps these rules to two classes, beating stdlib's one-class
154
+ embedded defaults cleanly. */
155
+ .dark .stdlib-chart {
156
+ color: rgb(228 228 231); /* zinc-200 — readable on zinc-950 */
157
+ }
158
+ .dark .stdlib-chart .stdlib-chart-slice,
159
+ .dark .stdlib-chart .stdlib-chart-point,
160
+ .dark .stdlib-chart .stdlib-chart-box-outlier {
161
+ /* zinc-950 to match the dark page background, so slice / point
162
+ separators read as "gaps between segments" rather than visible
163
+ white lines against a dark surface. */
164
+ stroke: rgb(9 9 11);
165
+ }
166
+
167
+ /* stdlib charts — scientific data-viz refinement.
168
+
169
+ stdlib already renders the structure (gridlines, axes, ticks, flat
170
+ fills); the only thing missing for a precise "measurement" look is
171
+ the typography. Numeric axis ticks + bar values read as monospaced,
172
+ tabular figures so columns of numbers line up the way they do in a
173
+ plotting tool. Two-class selectors (`.stdlib-chart .stdlib-chart-…`)
174
+ beat stdlib's one-class embedded <style> the same way the dark-mode
175
+ overrides above do. font-family is otherwise unset by stdlib, so the
176
+ chart would inherit the app sans — we pin the numeric labels to the
177
+ platform mono stack (IBM Plex Mono via @fontsource). */
178
+ .stdlib-chart .stdlib-chart-tick-label,
179
+ .stdlib-chart .stdlib-chart-bar-value,
180
+ .stdlib-chart .stdlib-chart-axis-label {
181
+ font-family: var(--font-mono);
182
+ font-variant-numeric: tabular-nums;
183
+ }
184
+
59
185
  *::-webkit-scrollbar {
60
186
  width: 0.5rem;
61
187
  height: 0.5rem;
@@ -89,20 +215,31 @@
89
215
  background: transparent;
90
216
  }
91
217
 
218
+ .multi-select-pill-strip {
219
+ scrollbar-width: thin;
220
+ }
221
+
92
222
  body {
93
223
  /* Explicit values — avoids @apply layer ordering issues with multiple CSS files */
94
224
  background-color: rgb(250 250 250); /* zinc-50 */
95
- color: rgb(24 24 27); /* zinc-900 */
225
+ color: rgb(24 24 27); /* zinc-900 */
96
226
  }
97
227
 
98
228
  .dark body {
99
- background-color: rgb(9 9 11); /* zinc-950 */
100
- color: rgb(244 244 245); /* zinc-100 */
229
+ background-color: rgb(9 9 11); /* zinc-950 */
230
+ color: rgb(244 244 245); /* zinc-100 */
101
231
  }
102
232
 
103
233
  .dark {
104
- --theme-shadow-elevated: none;
105
- --theme-shadow-hover: none;
234
+ color-scheme: dark;
235
+ --theme-bevel-top: inset 0 1px 0 0 rgb(255 255 255 / 0.07);
236
+ --theme-bevel-bottom: inset 0 -1px 1px 0 rgb(0 0 0 / 0.4);
237
+ --theme-recess: inset 0 1px 2px rgb(0 0 0 / 0.45);
238
+ --theme-recess-sm: inset 0 1px 1px rgb(0 0 0 / 0.4);
239
+ --theme-press: inset 0 2px 4px rgb(0 0 0 / 0.5), inset 0 1px 1px rgb(0 0 0 / 0.3);
240
+ --theme-shadow-float: 0 14px 36px -10px rgb(0 0 0 / 0.6), 0 4px 12px -4px rgb(0 0 0 / 0.4);
241
+ --theme-shadow-elevated: var(--theme-bevel-top), var(--theme-bevel-bottom);
242
+ --theme-shadow-hover: var(--theme-bevel-top), var(--theme-bevel-bottom), 0 6px 18px -6px rgb(0 0 0 / 0.5);
106
243
  --theme-list-active-bg: rgb(39 39 42);
107
244
  --theme-list-hover-bg: rgb(30 30 33);
108
245
  --theme-tab-active-bg: rgb(39 39 42);
@@ -17,6 +17,7 @@ button[disabled],
17
17
  /* Global Checkbox Styling */
18
18
  input[type="checkbox"] {
19
19
  @apply appearance-none w-4 h-4 bg-white dark:bg-black border border-zinc-300 dark:border-zinc-700 cursor-pointer transition-all duration-200 inline-flex items-center justify-center m-0 align-middle rounded;
20
+ box-shadow: var(--theme-recess-sm);
20
21
  }
21
22
 
22
23
  input[type="checkbox"]::before {
@@ -27,6 +28,7 @@ input[type="checkbox"]::before {
27
28
 
28
29
  input[type="checkbox"]:checked {
29
30
  @apply bg-blue-500 border-blue-500;
31
+ box-shadow: inset 0 1px 0 0 rgb(255 255 255 / 0.35);
30
32
  }
31
33
 
32
34
  input[type="checkbox"]:checked::before {
@@ -42,7 +44,7 @@ input[type="checkbox"]:focus {
42
44
  }
43
45
 
44
46
  input[type="checkbox"]:focus-visible {
45
- @apply outline-none ring-2 ring-blue-500 ring-offset-2 dark:ring-offset-zinc-900;
47
+ @apply focus-ui;
46
48
  }
47
49
 
48
50
  input[type="checkbox"]:disabled {