From fc808b81c7dbc10f7483896426945b683e71364d Mon Sep 17 00:00:00 2001 From: SirBlobby Date: Sun, 9 Aug 2026 17:09:06 -0400 Subject: [PATCH] Add shared UI package and logo Widget surface, annotation overlay, whiteboard and video components used by both the website and the kiosk. Claude-Session: https://claude.ai/code/session_01SS9F92jb51bMCRCKem6QtD --- assets/logo.svg | 24 +++ packages/ui/package.json | 32 ++++ packages/ui/src/AnnotationOverlay.svelte | 119 +++++++++++++++ packages/ui/src/Logo.svelte | 35 +++++ packages/ui/src/NightSurface.svelte | 58 ++++++++ packages/ui/src/TrackVideo.svelte | 26 ++++ packages/ui/src/WhiteboardCanvas.svelte | 124 ++++++++++++++++ packages/ui/src/WidgetRenderer.svelte | 57 ++++++++ packages/ui/src/WidgetSurface.svelte | 146 +++++++++++++++++++ packages/ui/src/index.ts | 14 ++ packages/ui/src/widgets/AgendaWidget.svelte | 31 ++++ packages/ui/src/widgets/ClockWidget.svelte | 52 +++++++ packages/ui/src/widgets/CustomWidget.svelte | 92 ++++++++++++ packages/ui/src/widgets/ImageWidget.svelte | 17 +++ packages/ui/src/widgets/PinWidget.svelte | 48 ++++++ packages/ui/src/widgets/TextWidget.svelte | 24 +++ packages/ui/src/widgets/WeatherWidget.svelte | 94 ++++++++++++ 17 files changed, 993 insertions(+) create mode 100644 assets/logo.svg create mode 100644 packages/ui/package.json create mode 100644 packages/ui/src/AnnotationOverlay.svelte create mode 100644 packages/ui/src/Logo.svelte create mode 100644 packages/ui/src/NightSurface.svelte create mode 100644 packages/ui/src/TrackVideo.svelte create mode 100644 packages/ui/src/WhiteboardCanvas.svelte create mode 100644 packages/ui/src/WidgetRenderer.svelte create mode 100644 packages/ui/src/WidgetSurface.svelte create mode 100644 packages/ui/src/index.ts create mode 100644 packages/ui/src/widgets/AgendaWidget.svelte create mode 100644 packages/ui/src/widgets/ClockWidget.svelte create mode 100644 packages/ui/src/widgets/CustomWidget.svelte create mode 100644 packages/ui/src/widgets/ImageWidget.svelte create mode 100644 packages/ui/src/widgets/PinWidget.svelte create mode 100644 packages/ui/src/widgets/TextWidget.svelte create mode 100644 packages/ui/src/widgets/WeatherWidget.svelte diff --git a/assets/logo.svg b/assets/logo.svg new file mode 100644 index 0000000..166d7b2 --- /dev/null +++ b/assets/logo.svg @@ -0,0 +1,24 @@ + + PiStation + + + + + + + + + + + + + + diff --git a/packages/ui/package.json b/packages/ui/package.json new file mode 100644 index 0000000..f5dd434 --- /dev/null +++ b/packages/ui/package.json @@ -0,0 +1,32 @@ +{ + "name": "@pistation/ui", + "version": "0.1.0", + "private": true, + "type": "module", + "main": "./src/index.ts", + "types": "./src/index.ts", + "svelte": "./src/index.ts", + "exports": { + ".": { + "svelte": "./src/index.ts", + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@pistation/client-core": "workspace:*", + "@pistation/shared-types": "workspace:*" + }, + "peerDependencies": { + "@excalidraw/excalidraw": "^0.17.6", + "@iconify/svelte": "^4.0.2", + "livekit-client": "^2.7.2", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "svelte": "^5.0.0" + }, + "devDependencies": { + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1" + } +} diff --git a/packages/ui/src/AnnotationOverlay.svelte b/packages/ui/src/AnnotationOverlay.svelte new file mode 100644 index 0000000..9cdea4d --- /dev/null +++ b/packages/ui/src/AnnotationOverlay.svelte @@ -0,0 +1,119 @@ + + + diff --git a/packages/ui/src/Logo.svelte b/packages/ui/src/Logo.svelte new file mode 100644 index 0000000..d4c43d2 --- /dev/null +++ b/packages/ui/src/Logo.svelte @@ -0,0 +1,35 @@ + + + + PiStation + + + + + + + + + + + + + + diff --git a/packages/ui/src/NightSurface.svelte b/packages/ui/src/NightSurface.svelte new file mode 100644 index 0000000..8d26a1a --- /dev/null +++ b/packages/ui/src/NightSurface.svelte @@ -0,0 +1,58 @@ + + +
+

+ {timeLabel} +

+ + {#if layout.nightMode.showPin && pin} +

+ {formatPin(pin)} +

+ {/if} +
diff --git a/packages/ui/src/TrackVideo.svelte b/packages/ui/src/TrackVideo.svelte new file mode 100644 index 0000000..04e5a18 --- /dev/null +++ b/packages/ui/src/TrackVideo.svelte @@ -0,0 +1,26 @@ + + + diff --git a/packages/ui/src/WhiteboardCanvas.svelte b/packages/ui/src/WhiteboardCanvas.svelte new file mode 100644 index 0000000..0f7bc71 --- /dev/null +++ b/packages/ui/src/WhiteboardCanvas.svelte @@ -0,0 +1,124 @@ + + +
(isDrawing = true)} + onpointerupcapture={() => (isDrawing = false)} + onpointercancelcapture={() => (isDrawing = false)} +>
diff --git a/packages/ui/src/WidgetRenderer.svelte b/packages/ui/src/WidgetRenderer.svelte new file mode 100644 index 0000000..b557fad --- /dev/null +++ b/packages/ui/src/WidgetRenderer.svelte @@ -0,0 +1,57 @@ + + +{#if widget.kind === "clock"} + +{:else if widget.kind === "weather"} + +{:else if widget.kind === "pin"} + +{:else if widget.kind === "text"} + +{:else if widget.kind === "image"} + +{:else if widget.kind === "agenda"} + +{:else if widget.kind === "custom"} + +{/if} diff --git a/packages/ui/src/WidgetSurface.svelte b/packages/ui/src/WidgetSurface.svelte new file mode 100644 index 0000000..af8d702 --- /dev/null +++ b/packages/ui/src/WidgetSurface.svelte @@ -0,0 +1,146 @@ + + +
+ {#if hasWallpaper} + {#each wallpapers as wallpaper, index (wallpaper)} + + {/each} +
+ {/if} + +
+ {#each visibleWidgets as widget (widget.widgetId)} + {@const style = styleOf(widget)} + {@const interaction = onSelect + ? { + role: "button", + tabindex: 0, + onclick: () => onSelect(widget.widgetId), + onkeydown: (event: KeyboardEvent) => { + if (event.key === "Enter") onSelect(widget.widgetId); + } + } + : {}} +
+
+ +
+
+ {/each} +
+
diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts new file mode 100644 index 0000000..107ca4f --- /dev/null +++ b/packages/ui/src/index.ts @@ -0,0 +1,14 @@ +export { default as AnnotationOverlay } from "./AnnotationOverlay.svelte"; +export { default as Logo } from "./Logo.svelte"; +export { default as NightSurface } from "./NightSurface.svelte"; +export { default as TrackVideo } from "./TrackVideo.svelte"; +export { default as WhiteboardCanvas } from "./WhiteboardCanvas.svelte"; +export { default as WidgetSurface } from "./WidgetSurface.svelte"; +export { default as WidgetRenderer } from "./WidgetRenderer.svelte"; +export { default as ClockWidget } from "./widgets/ClockWidget.svelte"; +export { default as WeatherWidget } from "./widgets/WeatherWidget.svelte"; +export { default as PinWidget } from "./widgets/PinWidget.svelte"; +export { default as TextWidget } from "./widgets/TextWidget.svelte"; +export { default as ImageWidget } from "./widgets/ImageWidget.svelte"; +export { default as AgendaWidget } from "./widgets/AgendaWidget.svelte"; +export { default as CustomWidget } from "./widgets/CustomWidget.svelte"; diff --git a/packages/ui/src/widgets/AgendaWidget.svelte b/packages/ui/src/widgets/AgendaWidget.svelte new file mode 100644 index 0000000..8510953 --- /dev/null +++ b/packages/ui/src/widgets/AgendaWidget.svelte @@ -0,0 +1,31 @@ + + +
+ {#if settings.heading} +

+ {settings.heading} +

+ {/if} + +
    + {#each settings.items as item} +
  • + + {item} +
  • + {/each} +
+
diff --git a/packages/ui/src/widgets/ClockWidget.svelte b/packages/ui/src/widgets/ClockWidget.svelte new file mode 100644 index 0000000..0866619 --- /dev/null +++ b/packages/ui/src/widgets/ClockWidget.svelte @@ -0,0 +1,52 @@ + + +
+

+ {timeLabel} +

+ {#if settings.showDate} +

+ {dateLabel} +

+ {/if} +
diff --git a/packages/ui/src/widgets/CustomWidget.svelte b/packages/ui/src/widgets/CustomWidget.svelte new file mode 100644 index 0000000..17665db --- /dev/null +++ b/packages/ui/src/widgets/CustomWidget.svelte @@ -0,0 +1,92 @@ + + +{#if !definition} +
+ Widget definition missing +
+{:else} +
+ {#each definition.blocks as block (block.blockId)} + {#if block.kind === "heading"} +

+ {renderTemplate(block.template, data)} +

+ {:else if block.kind === "text"} +

+ {renderTemplate(block.template, data)} +

+ {:else if block.kind === "metric"} +
+ + {renderTemplate(block.labelTemplate, data)} + + + {renderTemplate(block.valueTemplate, data)}{block.unit} + +
+ {:else if block.kind === "list"} +
    + {#each listItems(block.sourcePath, block.maxItems) as item} +
  • + {renderTemplate(block.itemTemplate, item)} +
  • + {/each} +
+ {:else if block.kind === "image"} + + {:else if block.kind === "divider"} +
+ {/if} + {/each} +
+{/if} diff --git a/packages/ui/src/widgets/ImageWidget.svelte b/packages/ui/src/widgets/ImageWidget.svelte new file mode 100644 index 0000000..6943589 --- /dev/null +++ b/packages/ui/src/widgets/ImageWidget.svelte @@ -0,0 +1,17 @@ + + +{#if settings.imageUrl} + +{:else} +
No image set
+{/if} diff --git a/packages/ui/src/widgets/PinWidget.svelte b/packages/ui/src/widgets/PinWidget.svelte new file mode 100644 index 0000000..96924ac --- /dev/null +++ b/packages/ui/src/widgets/PinWidget.svelte @@ -0,0 +1,48 @@ + + +
+

+ {settings.label} +

+ + {#if settings.showJoinUrl} +

+ {joinUrl} +

+ {/if} + + {#if pin} +

+ {formatPin(pin)} +

+ {:else} +

+ ------ +

+ {/if} +
diff --git a/packages/ui/src/widgets/TextWidget.svelte b/packages/ui/src/widgets/TextWidget.svelte new file mode 100644 index 0000000..9dbabd1 --- /dev/null +++ b/packages/ui/src/widgets/TextWidget.svelte @@ -0,0 +1,24 @@ + + +
+ {#if settings.heading} +

+ {settings.heading} +

+ {/if} + {#if settings.body} +

+ {settings.body} +

+ {/if} +
diff --git a/packages/ui/src/widgets/WeatherWidget.svelte b/packages/ui/src/widgets/WeatherWidget.svelte new file mode 100644 index 0000000..b641506 --- /dev/null +++ b/packages/ui/src/widgets/WeatherWidget.svelte @@ -0,0 +1,94 @@ + + +
+ + + + +
+ {#if reading} +

+ {reading.temperature}°{unitLabel} +

+ {:else} +

+ {hasFailed ? "Weather unavailable" : "Loading"} +

+ {/if} +

+ {settings.locationLabel} +

+
+