@yourrealm/sdk 0.0.0-alpha.1378.ce03c8dc

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 (230) hide show
  1. package/LICENSE-EXCEPTION +26 -0
  2. package/README.md +34 -0
  3. package/dist/chunk-3R6KEFOI.js +22 -0
  4. package/dist/chunk-67BDAMYX.js +376 -0
  5. package/dist/chunk-AITGS27R.js +139 -0
  6. package/dist/chunk-CMAEHQ4F.js +6 -0
  7. package/dist/chunk-MLKGABMK.js +9 -0
  8. package/dist/chunk-O3UIK34B.js +1430 -0
  9. package/dist/chunk-VHTUSZUX.js +176 -0
  10. package/dist/cli.js +9006 -0
  11. package/dist/docker.js +54 -0
  12. package/dist/index.js +1053 -0
  13. package/dist/ingress.js +436 -0
  14. package/dist/jsx-runtime.js +2 -0
  15. package/dist/preact.js +22 -0
  16. package/dist/service-url.js +11 -0
  17. package/dist/services.js +213 -0
  18. package/dist/test.js +5069 -0
  19. package/dist/ui.js +254 -0
  20. package/dist/worker-protocol.js +89 -0
  21. package/package.json +112 -0
  22. package/src/app-icon.ts +74 -0
  23. package/src/apply.ts +52 -0
  24. package/src/bundle-ui.ts +211 -0
  25. package/src/bundle.ts +546 -0
  26. package/src/cli/build-dev-landing.ts +84 -0
  27. package/src/cli/build-install-panel.ts +90 -0
  28. package/src/cli/build-runtime.ts +133 -0
  29. package/src/cli/build.ts +63 -0
  30. package/src/cli/dev.ts +107 -0
  31. package/src/cli/down.ts +60 -0
  32. package/src/cli/main.ts +56 -0
  33. package/src/cli/test.ts +61 -0
  34. package/src/cli/validate.ts +37 -0
  35. package/src/compile.ts +100 -0
  36. package/src/create-app.ts +982 -0
  37. package/src/deep-merge.ts +33 -0
  38. package/src/dev/fixture.ts +48 -0
  39. package/src/dev/install-handler.ts +383 -0
  40. package/src/dev/server.ts +968 -0
  41. package/src/dev/stack.ts +108 -0
  42. package/src/dev/state.ts +159 -0
  43. package/src/docker.ts +96 -0
  44. package/src/errors.ts +61 -0
  45. package/src/factory.ts +481 -0
  46. package/src/garage.ts +56 -0
  47. package/src/generator/hash.ts +6 -0
  48. package/src/generator/resolve-refs.ts +207 -0
  49. package/src/grants-builder.ts +100 -0
  50. package/src/grants.ts +260 -0
  51. package/src/home-api.ts +172 -0
  52. package/src/home-cookie.ts +10 -0
  53. package/src/home-registry.ts +120 -0
  54. package/src/home.ts +187 -0
  55. package/src/host-registry.ts +143 -0
  56. package/src/http-header.ts +20 -0
  57. package/src/identifiers.ts +49 -0
  58. package/src/index.ts +66 -0
  59. package/src/info.ts +64 -0
  60. package/src/ini.ts +50 -0
  61. package/src/json.ts +14 -0
  62. package/src/jsx-runtime/index.ts +5 -0
  63. package/src/keys.ts +29 -0
  64. package/src/mysql.ts +56 -0
  65. package/src/pack-build.ts +108 -0
  66. package/src/pack.ts +115 -0
  67. package/src/peer-registry.ts +121 -0
  68. package/src/peer-world.ts +117 -0
  69. package/src/permission-meta.ts +116 -0
  70. package/src/permissions.ts +108 -0
  71. package/src/pkg-root.ts +34 -0
  72. package/src/postgres.ts +79 -0
  73. package/src/preact/index.ts +27 -0
  74. package/src/redis.ts +43 -0
  75. package/src/ref.ts +271 -0
  76. package/src/remote-registry.ts +33 -0
  77. package/src/required-host-kinds.ts +110 -0
  78. package/src/resolve-context.ts +111 -0
  79. package/src/resolve.ts +471 -0
  80. package/src/runtime/docker/compose/compose-schema.ts +118 -0
  81. package/src/runtime/docker/compose/convert-to-docker-compose.ts +278 -0
  82. package/src/runtime/docker/compose/default-hardening.ts +21 -0
  83. package/src/runtime/docker/compose/effects.ts +107 -0
  84. package/src/runtime/docker/compose/index.ts +17 -0
  85. package/src/runtime/docker/compose/labels.ts +39 -0
  86. package/src/runtime/docker/compose/types.ts +78 -0
  87. package/src/runtime/docker/home-proxy-server/index.ts +177 -0
  88. package/src/runtime/docker/home-proxy-server/protocol.ts +53 -0
  89. package/src/runtime/docker/ingress/build-config.ts +819 -0
  90. package/src/runtime/docker/ingress/index.ts +16 -0
  91. package/src/runtime/docker/ingress/types.ts +182 -0
  92. package/src/runtime/docker/log-frames.ts +121 -0
  93. package/src/runtime/docker/logs-server/index.ts +366 -0
  94. package/src/runtime/docker/spawn-server/context-key.ts +82 -0
  95. package/src/runtime/docker/spawn-server/index.ts +492 -0
  96. package/src/runtime/docker/spawn-server/protocol.ts +70 -0
  97. package/src/runtime/docker/spawn-server/spec.ts +122 -0
  98. package/src/runtime/docker/stats-server/disks.ts +161 -0
  99. package/src/runtime/docker/stats-server/index.ts +322 -0
  100. package/src/runtime/home-client/index.ts +71 -0
  101. package/src/runtime/log-line.ts +24 -0
  102. package/src/runtime/logs-client/index.ts +175 -0
  103. package/src/runtime/spawn-client/index.ts +93 -0
  104. package/src/runtime/spawn-client/kaniko.ts +79 -0
  105. package/src/runtime/stats-client/index.ts +65 -0
  106. package/src/runtime/stats-sample.ts +95 -0
  107. package/src/safe-json.ts +37 -0
  108. package/src/sandbox.ts +1209 -0
  109. package/src/schema/build-apps.ts +38 -0
  110. package/src/schema/home.ts +217 -0
  111. package/src/schema/host.ts +792 -0
  112. package/src/schema/inputs.ts +26 -0
  113. package/src/schema/nodes.ts +75 -0
  114. package/src/schema/peer.ts +400 -0
  115. package/src/schema/public-domain.ts +116 -0
  116. package/src/schema/remote.ts +74 -0
  117. package/src/security.ts +594 -0
  118. package/src/serialize.ts +21 -0
  119. package/src/service-definition.ts +284 -0
  120. package/src/service-url.ts +64 -0
  121. package/src/service.ts +39 -0
  122. package/src/services/index.ts +5 -0
  123. package/src/shared.ts +65 -0
  124. package/src/speculative-silo.ts +96 -0
  125. package/src/stable-secret.ts +14 -0
  126. package/src/surfaces.ts +264 -0
  127. package/src/tailwind-bin.ts +56 -0
  128. package/src/test/boot.ts +192 -0
  129. package/src/test/compile.ts +501 -0
  130. package/src/test/describe.ts +117 -0
  131. package/src/test/index.ts +11 -0
  132. package/src/test/ingress-fixture.ts +37 -0
  133. package/src/test/types.ts +318 -0
  134. package/src/trusted-headers.ts +79 -0
  135. package/src/types.ts +126 -0
  136. package/src/ui/components/aside.tsx +96 -0
  137. package/src/ui/components/badge.tsx +42 -0
  138. package/src/ui/components/button.tsx +59 -0
  139. package/src/ui/components/card.tsx +106 -0
  140. package/src/ui/components/checkbox.tsx +37 -0
  141. package/src/ui/components/code-block.tsx +29 -0
  142. package/src/ui/components/combobox.tsx +119 -0
  143. package/src/ui/components/config-field.tsx +284 -0
  144. package/src/ui/components/dialog.tsx +137 -0
  145. package/src/ui/components/domain-editor.tsx +317 -0
  146. package/src/ui/components/domain-list.ts +205 -0
  147. package/src/ui/components/dropdown-menu.tsx +73 -0
  148. package/src/ui/components/empty-state.tsx +51 -0
  149. package/src/ui/components/highlight.ts +59 -0
  150. package/src/ui/components/icons.tsx +101 -0
  151. package/src/ui/components/image-posture.tsx +85 -0
  152. package/src/ui/components/input.tsx +28 -0
  153. package/src/ui/components/label.tsx +16 -0
  154. package/src/ui/components/line-chart.tsx +113 -0
  155. package/src/ui/components/page.tsx +39 -0
  156. package/src/ui/components/permission-row.tsx +262 -0
  157. package/src/ui/components/permission-section.tsx +51 -0
  158. package/src/ui/components/pie-chart.tsx +245 -0
  159. package/src/ui/components/schema-block.tsx +60 -0
  160. package/src/ui/components/separator.tsx +42 -0
  161. package/src/ui/components/sheet.tsx +130 -0
  162. package/src/ui/components/sidebar.tsx +650 -0
  163. package/src/ui/components/silo-picker.tsx +510 -0
  164. package/src/ui/components/skeleton.tsx +13 -0
  165. package/src/ui/components/sonner.tsx +33 -0
  166. package/src/ui/components/switch.tsx +58 -0
  167. package/src/ui/components/table-view.ts +219 -0
  168. package/src/ui/components/table.tsx +506 -0
  169. package/src/ui/components/tabs.tsx +59 -0
  170. package/src/ui/components/timeago.tsx +84 -0
  171. package/src/ui/components/tooltip.tsx +56 -0
  172. package/src/ui/components.json +21 -0
  173. package/src/ui/dev/dev-shell.tsx +145 -0
  174. package/src/ui/home/home-grid.tsx +177 -0
  175. package/src/ui/hooks/use-mobile.ts +19 -0
  176. package/src/ui/index.ts +189 -0
  177. package/src/ui/install/adapter.ts +193 -0
  178. package/src/ui/install/deploy-targets-picker.tsx +185 -0
  179. package/src/ui/install/install-app-panel.tsx +912 -0
  180. package/src/ui/install/install-error.tsx +62 -0
  181. package/src/ui/install/perms-inactive.ts +50 -0
  182. package/src/ui/install/required-fields.ts +37 -0
  183. package/src/ui/install/tabs-form.tsx +557 -0
  184. package/src/ui/tokens.css +279 -0
  185. package/src/ui/utils.ts +78 -0
  186. package/src/ui-error.ts +139 -0
  187. package/src/ui-runtime/dev-landing-entry.tsx +258 -0
  188. package/src/ui-runtime/index.ts +83 -0
  189. package/src/ui-runtime/install-entry.tsx +119 -0
  190. package/src/ui-runtime/mount.ts +367 -0
  191. package/src/ui-runtime/runtime.css +56 -0
  192. package/src/ui-surface.ts +451 -0
  193. package/src/valkey.ts +43 -0
  194. package/src/worker/extract-tar.ts +125 -0
  195. package/src/worker/identity.ts +183 -0
  196. package/src/worker/index.ts +16 -0
  197. package/src/worker/public-ip.ts +92 -0
  198. package/src/worker/queue.ts +54 -0
  199. package/src/worker/reconcile.ts +127 -0
  200. package/src/worker/start-worker.ts +227 -0
  201. package/src/worker/ui-proxy.ts +335 -0
  202. package/src/worker/uplink.ts +523 -0
  203. package/src/worker/validate.ts +34 -0
  204. package/src/worker/verify-relay.ts +102 -0
  205. package/src/worker-protocol/app-dirs.ts +94 -0
  206. package/src/worker-protocol/home-proxy-paths.ts +76 -0
  207. package/src/worker-protocol/index.ts +55 -0
  208. package/src/worker-protocol/ingress.ts +166 -0
  209. package/src/worker-protocol/logs-socket-path.ts +50 -0
  210. package/src/worker-protocol/networks.ts +39 -0
  211. package/src/worker-protocol/node-version.ts +70 -0
  212. package/src/worker-protocol/silo-paths.ts +114 -0
  213. package/src/worker-protocol/spawn-paths.ts +165 -0
  214. package/src/worker-protocol/stats-socket-path.ts +40 -0
  215. package/src/worker-protocol/update-files.ts +77 -0
  216. package/src/worker-protocol/uplink/constants.ts +86 -0
  217. package/src/worker-protocol/uplink/errors.ts +61 -0
  218. package/src/worker-protocol/uplink/frame.ts +114 -0
  219. package/src/worker-protocol/uplink/identity.ts +40 -0
  220. package/src/worker-protocol/uplink/index.ts +63 -0
  221. package/src/worker-protocol/uplink/methods.ts +462 -0
  222. package/src/worker-protocol/uplink/mux.ts +646 -0
  223. package/src/worker-protocol/uplink/protocol.ts +11 -0
  224. package/src/worker-protocol/uplink/rpc.ts +309 -0
  225. package/src/worker-protocol/uplink/session.ts +540 -0
  226. package/src/worker-protocol/uplink/test-support.ts +188 -0
  227. package/src/worker-protocol/worker-mount.ts +27 -0
  228. package/src/worker-protocol/worker-state.ts +190 -0
  229. package/src/xml.ts +49 -0
  230. package/src/yaml.ts +15 -0
@@ -0,0 +1,73 @@
1
+ import type { ComponentProps } from "preact";
2
+ import { Menu as MenuPrimitive } from "@ark-ui/react/menu";
3
+
4
+ import { cn } from "../utils.ts";
5
+
6
+ function DropdownMenu(props: ComponentProps<typeof MenuPrimitive.Root>) {
7
+ return <MenuPrimitive.Root {...props} />;
8
+ }
9
+
10
+ function DropdownMenuTrigger(props: ComponentProps<typeof MenuPrimitive.Trigger>) {
11
+ return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
12
+ }
13
+
14
+ // Opens the menu on right-click (desktop) or long-press (touch), anchored at
15
+ // the pointer. Wrap the element the menu belongs to with `asChild`.
16
+ function DropdownMenuContextTrigger(props: ComponentProps<typeof MenuPrimitive.ContextTrigger>) {
17
+ return <MenuPrimitive.ContextTrigger data-slot="dropdown-menu-context-trigger" {...props} />;
18
+ }
19
+
20
+ function DropdownMenuContent({
21
+ className,
22
+ children,
23
+ align = "end",
24
+ ...props
25
+ }: ComponentProps<typeof MenuPrimitive.Content> & {
26
+ align?: "start" | "center" | "end";
27
+ }) {
28
+ return (
29
+ <MenuPrimitive.Positioner
30
+ className="z-50"
31
+ // Ark's Positioner reads positioning from props on Root; we expose
32
+ // a minimal `align` shortcut here. Anything fancier — offset,
33
+ // side, strategy — is set on Root via `positioning={...}`.
34
+ data-align={align}
35
+ >
36
+ <MenuPrimitive.Content
37
+ data-slot="dropdown-menu-content"
38
+ className={cn(
39
+ "z-50 min-w-[12rem] overflow-hidden rounded-xl border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none",
40
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
41
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
42
+ className,
43
+ )}
44
+ {...props}
45
+ >
46
+ {children}
47
+ </MenuPrimitive.Content>
48
+ </MenuPrimitive.Positioner>
49
+ );
50
+ }
51
+
52
+ function DropdownMenuItem({ className, ...props }: ComponentProps<typeof MenuPrimitive.Item>) {
53
+ return (
54
+ <MenuPrimitive.Item
55
+ data-slot="dropdown-menu-item"
56
+ className={cn(
57
+ "relative flex w-full cursor-pointer select-none items-center gap-2 rounded-lg px-2 py-1.5 text-sm outline-none transition-colors",
58
+ "data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground",
59
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
60
+ className,
61
+ )}
62
+ {...props}
63
+ />
64
+ );
65
+ }
66
+
67
+ export {
68
+ DropdownMenu,
69
+ DropdownMenuTrigger,
70
+ DropdownMenuContextTrigger,
71
+ DropdownMenuContent,
72
+ DropdownMenuItem,
73
+ };
@@ -0,0 +1,51 @@
1
+ import type { ComponentChildren, JSX } from "preact";
2
+ import { cn, type ElementProps } from "../utils.ts";
3
+
4
+ type EmptyStateProps = Omit<ElementProps<"div">, "title"> & {
5
+ /** Icon component from `icons.tsx`. Omitted renders text-only. */
6
+ icon?: (props: { className?: string }) => JSX.Element;
7
+ /** One short line saying what isn't here yet. */
8
+ title: string;
9
+ /** Optional second line saying how to make it exist. */
10
+ description?: ComponentChildren;
11
+ /** Optional call to action, usually the same Button the populated view has. */
12
+ action?: ComponentChildren;
13
+ };
14
+
15
+ /**
16
+ * The "nothing here yet" placeholder, sized for one or two lines.
17
+ *
18
+ * Nine of these were hand-rolled across the dashboard, at three different
19
+ * paddings and with three different opinions on whether an icon belongs, so
20
+ * an empty Domains page was a 170px dashed box holding eleven words. The
21
+ * padding here is deliberately smaller than what it replaced: an empty state
22
+ * says nothing, and nothing does not need a third of the viewport. Its
23
+ * measure is capped for the same reason `PageHeader`'s is — a line of prose
24
+ * stretched across a 1600px page is unreadable.
25
+ */
26
+ function EmptyState({
27
+ className,
28
+ icon: Icon,
29
+ title,
30
+ description,
31
+ action,
32
+ ...props
33
+ }: EmptyStateProps) {
34
+ return (
35
+ <div
36
+ data-slot="empty-state"
37
+ className={cn(
38
+ "flex flex-col items-center gap-2 rounded-2xl border border-dashed border-border bg-card/50 px-6 py-8 text-center",
39
+ className,
40
+ )}
41
+ {...props}
42
+ >
43
+ {Icon && <Icon className="size-7 text-muted-foreground/70" />}
44
+ <p class="text-sm font-medium text-foreground">{title}</p>
45
+ {description && <p class="max-w-prose text-sm text-muted-foreground">{description}</p>}
46
+ {action && <div class="mt-2 flex items-center gap-2">{action}</div>}
47
+ </div>
48
+ );
49
+ }
50
+
51
+ export { EmptyState };
@@ -0,0 +1,59 @@
1
+ import { codeToHtml, codeToTokens, type BundledLanguage } from "shiki";
2
+ import poimandres from "shiki/themes/poimandres.mjs";
3
+
4
+ const HTML_ESCAPES: Record<string, string> = {
5
+ "&": "&amp;",
6
+ "<": "&lt;",
7
+ ">": "&gt;",
8
+ '"': "&quot;",
9
+ };
10
+
11
+ function escapeHtml(text: string): string {
12
+ return text.replace(/[&<>"]/g, (c) => HTML_ESCAPES[c] ?? c);
13
+ }
14
+
15
+ /**
16
+ * Highlight a whole file and return one HTML string per line, so a caller
17
+ * that shows only some lines (a unified diff) can look them up by line
18
+ * number while multi-line tokens (block comments, template literals) still
19
+ * tokenize against their real context. Same theme as `highlightCode`.
20
+ * Server-only, for the reason above.
21
+ */
22
+ export async function highlightLines(
23
+ code: string,
24
+ lang: BundledLanguage = "typescript",
25
+ ): Promise<string[]> {
26
+ const { tokens } = await codeToTokens(code, { lang, theme: poimandres });
27
+ return tokens.map((line) =>
28
+ line
29
+ .map((token) => {
30
+ const style: string[] = [];
31
+ if (token.color) style.push(`color:${token.color}`);
32
+ // shiki's FontStyle bit flags: 1 italic, 2 bold, 4 underline.
33
+ const fs = token.fontStyle ?? 0;
34
+ if (fs & 1) style.push("font-style:italic");
35
+ if (fs & 2) style.push("font-weight:bold");
36
+ if (fs & 4) style.push("text-decoration:underline");
37
+ const content = escapeHtml(token.content);
38
+ return style.length > 0 ? `<span style="${style.join(";")}">${content}</span>` : content;
39
+ })
40
+ .join(""),
41
+ );
42
+ }
43
+
44
+ // Server-only. Kept out of code-block.tsx so the pure CodeBlock component can be
45
+ // imported by client islands (e.g. Home's WorkersPanel) without dragging Shiki +
46
+ // its oniguruma WASM into the client bundle - that WASM trips Home's CSP
47
+ // (script-src lacks unsafe-eval) and bloats the island chunk.
48
+ export async function highlightCode(code: string, lang = "typescript") {
49
+ const html = await codeToHtml(code, {
50
+ lang,
51
+ theme: poimandres,
52
+ });
53
+ // Strip the outer <pre><code>...</code></pre> wrapper and background color
54
+ // so the consuming component controls the container styling
55
+ return html
56
+ .replace(/^<pre[^>]*><code>/, "")
57
+ .replace(/<\/code><\/pre>$/, "")
58
+ .replace(/background-color:#[0-9a-fA-F]+;?/g, "");
59
+ }
@@ -0,0 +1,101 @@
1
+ // Named exports only — Vite/esbuild tree-shake by dropping unused names.
2
+ // `import * as` consumers (resolveIcon below) intentionally pull in all
3
+ // icons and form the registry of valid string names.
4
+
5
+ import type { JSX } from "preact";
6
+
7
+ // react-icons' IconBaseProps extends SVGAttributes which doesn't surface
8
+ // className/style under preact's typings. Merge in the props we use.
9
+ declare module "react-icons/lib" {
10
+ interface IconBaseProps {
11
+ className?: string;
12
+ style?: preact.JSX.CSSProperties | string;
13
+ }
14
+ }
15
+
16
+ export { PiArrowCircleUpDuotone as ArrowCircleUpIcon } from "react-icons/pi";
17
+ export { PiArrowsClockwiseDuotone as ArrowsClockwiseIcon } from "react-icons/pi";
18
+ export { PiArrowSquareOutDuotone as ArrowSquareOutIcon } from "react-icons/pi";
19
+ export { PiCaretDownBold as CaretDownIcon } from "react-icons/pi";
20
+ export { PiCaretLeftBold as CaretLeftIcon } from "react-icons/pi";
21
+ export { PiCaretRightBold as CaretRightIcon } from "react-icons/pi";
22
+ export { PiCaretUpBold as CaretUpIcon } from "react-icons/pi";
23
+ export { PiCaretUpDownBold as CaretUpDownIcon } from "react-icons/pi";
24
+ export { PiChartLineUpDuotone as ChartLineUpIcon } from "react-icons/pi";
25
+ export { PiCheckBold as CheckBoldIcon } from "react-icons/pi";
26
+ export { PiCheckCircleDuotone as CheckCircleIcon } from "react-icons/pi";
27
+ export { PiCheckDuotone as CheckIcon } from "react-icons/pi";
28
+ export { PiClipboardDuotone as ClipboardIcon } from "react-icons/pi";
29
+ export { PiClockDuotone as ClockIcon } from "react-icons/pi";
30
+ export { PiCloudArrowDownDuotone as CloudArrowDownIcon } from "react-icons/pi";
31
+ export { PiCloudSunDuotone as CloudSunIcon } from "react-icons/pi";
32
+ export { PiColumnsDuotone as ColumnsIcon } from "react-icons/pi";
33
+ export { PiCubeDuotone as CubeIcon } from "react-icons/pi";
34
+ export { PiDatabaseDuotone as DatabaseIcon } from "react-icons/pi";
35
+ export { PiDeviceMobileDuotone as DeviceMobileIcon } from "react-icons/pi";
36
+ export { PiDownloadSimpleDuotone as DownloadSimpleIcon } from "react-icons/pi";
37
+ export { PiEnvelopeDuotone as EnvelopeIcon } from "react-icons/pi";
38
+ export { PiEyeDuotone as EyeIcon } from "react-icons/pi";
39
+ export { PiEyeSlashDuotone as EyeSlashIcon } from "react-icons/pi";
40
+ export { PiFadersDuotone as FadersIcon } from "react-icons/pi";
41
+ export { PiFolderOpenDuotone as FolderOpenIcon } from "react-icons/pi";
42
+ export { PiFolderSimpleDuotone as FolderSimpleIcon } from "react-icons/pi";
43
+ export { PiGaugeDuotone as GaugeIcon } from "react-icons/pi";
44
+ export { PiGearDuotone as GearIcon } from "react-icons/pi";
45
+ export { PiGiftDuotone as GiftIcon } from "react-icons/pi";
46
+ export { PiGithubLogoDuotone as GithubLogoIcon } from "react-icons/pi";
47
+ export { PiGlobeDuotone as GlobeIcon } from "react-icons/pi";
48
+ export { PiGraphicsCardDuotone as GraphicsCardIcon } from "react-icons/pi";
49
+ export { PiHammerDuotone as HammerIcon } from "react-icons/pi";
50
+ export { PiHandshakeDuotone as HandshakeIcon } from "react-icons/pi";
51
+ export { PiHardDrivesDuotone as HardDrivesIcon } from "react-icons/pi";
52
+ export { PiHouseDuotone as HouseIcon } from "react-icons/pi";
53
+ export { PiInfoDuotone as InfoIcon } from "react-icons/pi";
54
+ export { PiKeyDuotone as KeyIcon } from "react-icons/pi";
55
+ export { PiLightbulbDuotone as LightbulbIcon } from "react-icons/pi";
56
+ export { PiListChecksDuotone as ListChecksIcon } from "react-icons/pi";
57
+ export { PiLockDuotone as LockIcon } from "react-icons/pi";
58
+ export { PiMagnifyingGlassDuotone as MagnifyingGlassIcon } from "react-icons/pi";
59
+ export { PiNotePencilDuotone as NotePencilIcon } from "react-icons/pi";
60
+ export { PiPackageDuotone as PackageIcon } from "react-icons/pi";
61
+ export { PiPencilSimpleDuotone as PencilSimpleIcon } from "react-icons/pi";
62
+ export { PiPlugDuotone as PlugIcon } from "react-icons/pi";
63
+ export { PiPlusDuotone as PlusIcon } from "react-icons/pi";
64
+ export { PiPulseDuotone as PulseIcon } from "react-icons/pi";
65
+ export { PiQuestionDuotone as QuestionIcon } from "react-icons/pi";
66
+ export { PiRocketLaunchDuotone as RocketLaunchIcon } from "react-icons/pi";
67
+ export { PiShareNetworkDuotone as ShareNetworkIcon } from "react-icons/pi";
68
+ export { PiShieldDuotone as ShieldIcon } from "react-icons/pi";
69
+ export { PiSidebarDuotone as SidebarIcon } from "react-icons/pi";
70
+ export { PiSignOutDuotone as SignOutIcon } from "react-icons/pi";
71
+ export { PiStackDuotone as StackIcon } from "react-icons/pi";
72
+ export { PiStarDuotone as StarIcon } from "react-icons/pi";
73
+ export { PiTerminalWindowDuotone as TerminalWindowIcon } from "react-icons/pi";
74
+ export { PiTextAlignCenterDuotone as TextAlignCenterIcon } from "react-icons/pi";
75
+ export { PiTextAlignLeftDuotone as TextAlignLeftIcon } from "react-icons/pi";
76
+ export { PiTextAlignRightDuotone as TextAlignRightIcon } from "react-icons/pi";
77
+ export { PiTrashDuotone as TrashIcon } from "react-icons/pi";
78
+ export { PiUserDuotone as UserIcon } from "react-icons/pi";
79
+ export { PiWarningDuotone as WarningIcon } from "react-icons/pi";
80
+ export { PiWarningOctagonDuotone as WarningOctagonIcon } from "react-icons/pi";
81
+ export { PiXBold as XBoldIcon } from "react-icons/pi";
82
+ export { PiXCircleDuotone as XCircleIcon } from "react-icons/pi";
83
+ export { PiXDuotone as XIcon } from "react-icons/pi";
84
+
85
+ // String-keyed lookup: surfaces store an icon name (e.g. `"CubeIcon"`)
86
+ // in the DB or in realm.tsx manifests instead of importing the component.
87
+ // `IconName` is the union of every named icon export above; `resolveIcon`
88
+ // looks the name up via `import * as` and falls back to `FadersIcon` on
89
+ // unknown/missing values.
90
+ import * as Icons from "./icons.tsx";
91
+
92
+ export type IconName = Exclude<keyof typeof Icons, "resolveIcon">;
93
+
94
+ export function resolveIcon(
95
+ name: string | undefined,
96
+ ): (props: { className?: string }) => JSX.Element {
97
+ if (name && name in Icons && name !== "resolveIcon") {
98
+ return Icons[name as IconName] as unknown as (props: { className?: string }) => JSX.Element;
99
+ }
100
+ return Icons.FadersIcon as unknown as (props: { className?: string }) => JSX.Element;
101
+ }
@@ -0,0 +1,85 @@
1
+ import type { ComponentChildren } from "preact";
2
+ import { CubeIcon, UserIcon, PlugIcon, FolderOpenIcon, ClockIcon } from "./icons.tsx";
3
+
4
+ type ImagePostureProps = {
5
+ image: string;
6
+ digest: string;
7
+ runsAs: string;
8
+ runsAsRoot?: boolean;
9
+ exposes: string[];
10
+ volumes: string[];
11
+ age: string;
12
+ };
13
+
14
+ function Field({
15
+ icon,
16
+ label,
17
+ children,
18
+ }: {
19
+ icon: ComponentChildren;
20
+ label: string;
21
+ children: ComponentChildren;
22
+ }) {
23
+ return (
24
+ <div className="flex items-center gap-2.5 text-sm">
25
+ <span className="text-muted-foreground">{icon}</span>
26
+ <span className="text-[11px] font-bold uppercase tracking-wide text-muted-foreground w-[78px]">
27
+ {label}
28
+ </span>
29
+ <span className="font-mono text-xs text-foreground">{children}</span>
30
+ </div>
31
+ );
32
+ }
33
+
34
+ function ImagePosture({
35
+ image,
36
+ digest,
37
+ runsAs,
38
+ runsAsRoot,
39
+ exposes,
40
+ volumes,
41
+ age,
42
+ }: ImagePostureProps) {
43
+ const shortDigest = digest.startsWith("sha256:") ? `${digest.slice(0, 19)}…` : digest;
44
+
45
+ return (
46
+ <div className="not-content bg-card rounded-3xl shadow-clay p-4 flex flex-col gap-3">
47
+ <div className="flex items-start gap-3">
48
+ <span className="text-perm mt-0.5">
49
+ <CubeIcon className="w-[22px] h-[22px]" />
50
+ </span>
51
+ <div className="flex-1 min-w-0">
52
+ <h3 className="text-sm font-bold text-foreground font-mono">{image}</h3>
53
+ <p className="text-[11px] mt-0.5 font-mono text-muted-foreground truncate">
54
+ {shortDigest}
55
+ </p>
56
+ </div>
57
+ </div>
58
+ <div className="flex flex-col gap-1.5 pl-0.5">
59
+ <Field icon={<UserIcon className="w-[18px] h-[18px]" />} label="Runs as">
60
+ <span className={runsAsRoot ? "text-danger font-bold" : ""}>{runsAs}</span>
61
+ </Field>
62
+ <Field icon={<PlugIcon className="w-[18px] h-[18px]" />} label="Exposes">
63
+ {exposes.length > 0 ? (
64
+ exposes.join(", ")
65
+ ) : (
66
+ <span className="text-muted-foreground">none</span>
67
+ )}
68
+ </Field>
69
+ <Field icon={<FolderOpenIcon className="w-[18px] h-[18px]" />} label="Volumes">
70
+ {volumes.length > 0 ? (
71
+ volumes.join(", ")
72
+ ) : (
73
+ <span className="text-muted-foreground">none</span>
74
+ )}
75
+ </Field>
76
+ <Field icon={<ClockIcon className="w-[18px] h-[18px]" />} label="Age">
77
+ {age}
78
+ </Field>
79
+ </div>
80
+ </div>
81
+ );
82
+ }
83
+
84
+ export { ImagePosture };
85
+ export type { ImagePostureProps };
@@ -0,0 +1,28 @@
1
+ import { cn, type ElementProps } from "../utils.ts";
2
+
3
+ type InputProps = ElementProps<"input"> & {
4
+ onValueChange?: (value: string) => void;
5
+ };
6
+
7
+ function Input({ className, type, onInput, onValueChange, ...props }: InputProps) {
8
+ const wrapped = onValueChange
9
+ ? (e: Event) => {
10
+ if (typeof onInput === "function") onInput(e as never);
11
+ onValueChange((e.currentTarget as HTMLInputElement).value);
12
+ }
13
+ : onInput;
14
+ return (
15
+ <input
16
+ type={type}
17
+ data-slot="input"
18
+ className={cn(
19
+ "h-8 w-full min-w-0 rounded-2xl border border-border bg-secondary px-2.5 py-1 text-base shadow-sm transition-all outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:shadow focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:shadow-clay-dark-sm dark:aria-invalid:ring-destructive/40",
20
+ className,
21
+ )}
22
+ {...props}
23
+ onInput={wrapped}
24
+ />
25
+ );
26
+ }
27
+
28
+ export { Input };
@@ -0,0 +1,16 @@
1
+ import { cn, type ElementProps } from "../utils.ts";
2
+
3
+ function Label({ className, ...props }: ElementProps<"label">) {
4
+ return (
5
+ <label
6
+ data-slot="label"
7
+ className={cn(
8
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
9
+ className,
10
+ )}
11
+ {...props}
12
+ />
13
+ );
14
+ }
15
+
16
+ export { Label };
@@ -0,0 +1,113 @@
1
+ // Line + area charts. A single SVG <path> over an evenly-spaced value series,
2
+ // drawn in a fixed viewBox and stretched to the container with
3
+ // `preserveAspectRatio="none"`, so it's responsive without measuring; the stroke
4
+ // keeps a constant width via `vector-effect="non-scaling-stroke"`. Colors default
5
+ // to `--chart-1`, so both theme with the surface. `AreaChart` is `LineChart` plus
6
+ // a translucent fill down to the baseline.
7
+
8
+ import { cn } from "../utils.ts";
9
+
10
+ interface LineChartProps {
11
+ /** y-values; x is the index (evenly spaced). Needs at least two points to draw. */
12
+ data: number[];
13
+ /** Rendered height in px. Width fills the container. Default 64. */
14
+ height?: number;
15
+ /** Stroke (and area fill) color. Default `--chart-1`. */
16
+ color?: string;
17
+ /** y-domain overrides; default to the data's own min/max. */
18
+ min?: number;
19
+ max?: number;
20
+ /** Stroke width in px (constant, not scaled). Default 2. */
21
+ strokeWidth?: number;
22
+ className?: string;
23
+ }
24
+
25
+ // Fixed vertical coordinate space; x spans 0..(len-1). preserveAspectRatio
26
+ // stretches both to the container, so the actual pixel values don't matter.
27
+ const VH = 100;
28
+
29
+ function linePath(data: number[], min?: number, max?: number): { d: string; vw: number } | null {
30
+ if (data.length < 2) return null;
31
+ const vw = data.length - 1;
32
+ const lo = min ?? Math.min(...data);
33
+ const hi = max ?? Math.max(...data);
34
+ const span = hi - lo || 1; // flat series → a centered horizontal line
35
+ const y = (v: number) => (VH - ((v - lo) / span) * VH).toFixed(2);
36
+ let d = `M 0 ${y(data[0]!)}`;
37
+ for (let i = 1; i < data.length; i++) d += ` L ${i} ${y(data[i]!)}`;
38
+ return { d, vw };
39
+ }
40
+
41
+ export function LineChart({
42
+ data,
43
+ height = 64,
44
+ color = "var(--chart-1)",
45
+ min,
46
+ max,
47
+ strokeWidth = 2,
48
+ className,
49
+ }: LineChartProps) {
50
+ const p = linePath(data, min, max);
51
+ return (
52
+ <svg
53
+ className={cn("block w-full", className)}
54
+ style={{ height }}
55
+ viewBox={`0 0 ${p ? p.vw : 1} ${VH}`}
56
+ preserveAspectRatio="none"
57
+ data-slot="line-chart"
58
+ >
59
+ {p && (
60
+ <path
61
+ d={p.d}
62
+ fill="none"
63
+ stroke={color}
64
+ stroke-width={strokeWidth}
65
+ stroke-linejoin="round"
66
+ stroke-linecap="round"
67
+ vector-effect="non-scaling-stroke"
68
+ />
69
+ )}
70
+ </svg>
71
+ );
72
+ }
73
+
74
+ export function AreaChart({
75
+ data,
76
+ height = 64,
77
+ color = "var(--chart-1)",
78
+ min,
79
+ max,
80
+ strokeWidth = 2,
81
+ className,
82
+ }: LineChartProps) {
83
+ const p = linePath(data, min, max);
84
+ return (
85
+ <svg
86
+ className={cn("block w-full", className)}
87
+ style={{ height }}
88
+ viewBox={`0 0 ${p ? p.vw : 1} ${VH}`}
89
+ preserveAspectRatio="none"
90
+ data-slot="area-chart"
91
+ >
92
+ {p && (
93
+ <path
94
+ d={`${p.d} L ${p.vw} ${VH} L 0 ${VH} Z`}
95
+ stroke="none"
96
+ fill={color}
97
+ fill-opacity={0.15}
98
+ />
99
+ )}
100
+ {p && (
101
+ <path
102
+ d={p.d}
103
+ fill="none"
104
+ stroke={color}
105
+ stroke-width={strokeWidth}
106
+ stroke-linejoin="round"
107
+ stroke-linecap="round"
108
+ vector-effect="non-scaling-stroke"
109
+ />
110
+ )}
111
+ </svg>
112
+ );
113
+ }
@@ -0,0 +1,39 @@
1
+ import type { ComponentChildren } from "preact";
2
+
3
+ interface PageHeaderProps {
4
+ title: string;
5
+ subtitle?: string;
6
+ actions?: ComponentChildren;
7
+ }
8
+
9
+ export function PageHeader({ title, subtitle, actions }: PageHeaderProps) {
10
+ return (
11
+ <header class="flex items-start justify-between gap-4 border-b border-border px-6 py-5">
12
+ <div class="flex flex-col gap-1">
13
+ <h1 class="text-2xl font-bold tracking-tight text-foreground">{title}</h1>
14
+ {subtitle && <p class="text-sm text-muted-foreground">{subtitle}</p>}
15
+ </div>
16
+ {actions && <div class="flex items-center gap-2">{actions}</div>}
17
+ </header>
18
+ );
19
+ }
20
+
21
+ interface PageProps extends PageHeaderProps {
22
+ children?: ComponentChildren;
23
+ }
24
+
25
+ /**
26
+ * Full-bleed page layout. Wraps a `PageHeader` over a scrollable body
27
+ * slot. Used inside ui surface iframes (page surfaces are full-bleed
28
+ * by default, the (authed) layout owns sidebar + breadcrumb chrome
29
+ * outside the iframe). Also reused inside Home for native dashboard
30
+ * pages so the look matches.
31
+ */
32
+ export function Page({ title, subtitle, actions, children }: PageProps) {
33
+ return (
34
+ <div class="flex h-full flex-col">
35
+ <PageHeader title={title} subtitle={subtitle} actions={actions} />
36
+ <main class="flex-1 overflow-auto">{children}</main>
37
+ </div>
38
+ );
39
+ }