@fungi.computer/caps 0.0.0-stage → 0.0.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.
- package/LICENSE +21 -0
- package/README.md +1125 -2
- package/dist/blocks/app-bar.d.ts +43 -0
- package/dist/blocks/app-bar.js +244 -0
- package/dist/blocks/app-status-bar.d.ts +93 -0
- package/dist/blocks/app-status-bar.js +42 -0
- package/dist/blocks/async-state.d.ts +21 -0
- package/dist/blocks/async-state.js +8 -0
- package/dist/blocks/card-grid.d.ts +11 -0
- package/dist/blocks/card-grid.js +21 -0
- package/dist/blocks/command-menu.d.ts +26 -0
- package/dist/blocks/command-menu.js +70 -0
- package/dist/blocks/composer.d.ts +23 -0
- package/dist/blocks/composer.js +13 -0
- package/dist/blocks/empty-state.d.ts +59 -0
- package/dist/blocks/empty-state.js +35 -0
- package/dist/blocks/faq-chat.d.ts +62 -0
- package/dist/blocks/faq-chat.js +77 -0
- package/dist/blocks/game-share-card.d.ts +17 -0
- package/dist/blocks/game-share-card.js +9 -0
- package/dist/blocks/launcher-grid.d.ts +50 -0
- package/dist/blocks/launcher-grid.js +11 -0
- package/dist/blocks/listing-detail.d.ts +23 -0
- package/dist/blocks/listing-detail.js +12 -0
- package/dist/blocks/loading-state.d.ts +5 -0
- package/dist/blocks/loading-state.js +8 -0
- package/dist/blocks/mosaic.d.ts +29 -0
- package/dist/blocks/mosaic.js +19 -0
- package/dist/blocks/permission-grid.d.ts +45 -0
- package/dist/blocks/permission-grid.js +14 -0
- package/dist/blocks/pricing-card.d.ts +53 -0
- package/dist/blocks/pricing-card.js +12 -0
- package/dist/blocks/release-tag.d.ts +20 -0
- package/dist/blocks/release-tag.js +10 -0
- package/dist/blocks/resource-select.d.ts +22 -0
- package/dist/blocks/resource-select.js +16 -0
- package/dist/blocks/result-state.d.ts +15 -0
- package/dist/blocks/result-state.js +8 -0
- package/dist/blocks/section-card.d.ts +31 -0
- package/dist/blocks/section-card.js +7 -0
- package/dist/blocks/section-heading.d.ts +13 -0
- package/dist/blocks/section-heading.js +11 -0
- package/dist/blocks/spec-sheet.d.ts +125 -0
- package/dist/blocks/spec-sheet.js +47 -0
- package/dist/blocks/status-tray.d.ts +83 -0
- package/dist/blocks/status-tray.js +79 -0
- package/dist/blocks/upload-queue.d.ts +48 -0
- package/dist/blocks/upload-queue.js +24 -0
- package/dist/components/action-row.d.ts +16 -0
- package/dist/components/action-row.js +11 -0
- package/dist/components/activity-line.d.ts +33 -0
- package/dist/components/activity-line.js +69 -0
- package/dist/components/alert.d.ts +17 -0
- package/dist/components/alert.js +17 -0
- package/dist/components/badge.d.ts +19 -0
- package/dist/components/badge.js +24 -0
- package/dist/components/button.d.ts +55 -0
- package/dist/components/button.js +230 -0
- package/dist/components/card.d.ts +61 -0
- package/dist/components/card.js +48 -0
- package/dist/components/chat-bubble.d.ts +34 -0
- package/dist/components/chat-bubble.js +10 -0
- package/dist/components/checkbox-bead.d.ts +6 -0
- package/dist/components/checkbox-bead.js +67 -0
- package/dist/components/checkbox.d.ts +21 -0
- package/dist/components/checkbox.js +46 -0
- package/dist/components/code-input.d.ts +35 -0
- package/dist/components/code-input.js +126 -0
- package/dist/components/combobox.d.ts +86 -0
- package/dist/components/combobox.js +218 -0
- package/dist/components/confirm-action.d.ts +23 -0
- package/dist/components/confirm-action.js +64 -0
- package/dist/components/copy-field.d.ts +53 -0
- package/dist/components/copy-field.js +49 -0
- package/dist/components/count-badge.d.ts +15 -0
- package/dist/components/count-badge.js +9 -0
- package/dist/components/cover-pattern.d.ts +19 -0
- package/dist/components/cover-pattern.js +74 -0
- package/dist/components/data-list.d.ts +25 -0
- package/dist/components/data-list.js +16 -0
- package/dist/components/dialog.d.ts +39 -0
- package/dist/components/dialog.js +73 -0
- package/dist/components/diff-view.d.ts +14 -0
- package/dist/components/diff-view.js +13 -0
- package/dist/components/disclosure.d.ts +29 -0
- package/dist/components/disclosure.js +19 -0
- package/dist/components/dropdown-menu.d.ts +49 -0
- package/dist/components/dropdown-menu.js +98 -0
- package/dist/components/field-guide.d.ts +41 -0
- package/dist/components/field-guide.js +18 -0
- package/dist/components/field.d.ts +164 -0
- package/dist/components/field.js +228 -0
- package/dist/components/game-feedback.d.ts +84 -0
- package/dist/components/game-feedback.js +48 -0
- package/dist/components/game-hud.d.ts +126 -0
- package/dist/components/game-hud.js +136 -0
- package/dist/components/generation-frame.d.ts +36 -0
- package/dist/components/generation-frame.js +97 -0
- package/dist/components/gooey.d.ts +32 -0
- package/dist/components/gooey.js +25 -0
- package/dist/components/hold-button.d.ts +26 -0
- package/dist/components/hold-button.js +115 -0
- package/dist/components/icon-button.d.ts +24 -0
- package/dist/components/icon-button.js +18 -0
- package/dist/components/icon-checkbox.d.ts +20 -0
- package/dist/components/icon-checkbox.js +9 -0
- package/dist/components/icon-choice-mark.d.ts +6 -0
- package/dist/components/icon-choice-mark.js +5 -0
- package/dist/components/icon-lamp.d.ts +42 -0
- package/dist/components/icon-lamp.js +15 -0
- package/dist/components/icon-radio.d.ts +16 -0
- package/dist/components/icon-radio.js +9 -0
- package/dist/components/inline-code.d.ts +3 -0
- package/dist/components/inline-code.js +7 -0
- package/dist/components/input-group.d.ts +9 -0
- package/dist/components/input-group.js +15 -0
- package/dist/components/input.d.ts +15 -0
- package/dist/components/input.js +15 -0
- package/dist/components/item-grid.d.ts +110 -0
- package/dist/components/item-grid.js +136 -0
- package/dist/components/keycap.d.ts +80 -0
- package/dist/components/keycap.js +18 -0
- package/dist/components/label.d.ts +13 -0
- package/dist/components/label.js +7 -0
- package/dist/components/listbox.d.ts +50 -0
- package/dist/components/listbox.js +134 -0
- package/dist/components/loader-blobs.d.ts +5 -0
- package/dist/components/loader-blobs.js +128 -0
- package/dist/components/loader.d.ts +21 -0
- package/dist/components/loader.js +25 -0
- package/dist/components/markdown.d.ts +23 -0
- package/dist/components/markdown.js +70 -0
- package/dist/components/minimap-labels.d.ts +39 -0
- package/dist/components/minimap-labels.js +113 -0
- package/dist/components/monogram.d.ts +32 -0
- package/dist/components/monogram.js +24 -0
- package/dist/components/motion-visibility.d.ts +2 -0
- package/dist/components/motion-visibility.js +25 -0
- package/dist/components/navigation-rail.d.ts +11 -0
- package/dist/components/navigation-rail.js +55 -0
- package/dist/components/overlay-root.d.ts +16 -0
- package/dist/components/overlay-root.js +24 -0
- package/dist/components/pixel-number.d.ts +50 -0
- package/dist/components/pixel-number.js +113 -0
- package/dist/components/popover.d.ts +31 -0
- package/dist/components/popover.js +24 -0
- package/dist/components/presence.d.ts +59 -0
- package/dist/components/presence.js +232 -0
- package/dist/components/price-tag.d.ts +11 -0
- package/dist/components/price-tag.js +6 -0
- package/dist/components/progress.d.ts +25 -0
- package/dist/components/progress.js +24 -0
- package/dist/components/prose.d.ts +14 -0
- package/dist/components/prose.js +10 -0
- package/dist/components/quota-meter.d.ts +24 -0
- package/dist/components/quota-meter.js +11 -0
- package/dist/components/radio-bead.d.ts +4 -0
- package/dist/components/radio-bead.js +53 -0
- package/dist/components/radio-group.d.ts +54 -0
- package/dist/components/radio-group.js +64 -0
- package/dist/components/resize-handle.d.ts +23 -0
- package/dist/components/resize-handle.js +31 -0
- package/dist/components/rough-drawing.d.ts +42 -0
- package/dist/components/rough-drawing.js +231 -0
- package/dist/components/rough.d.ts +68 -0
- package/dist/components/rough.js +33 -0
- package/dist/components/scroll-area.d.ts +12 -0
- package/dist/components/scroll-area.js +96 -0
- package/dist/components/scroll-motion.d.ts +12 -0
- package/dist/components/scroll-motion.js +31 -0
- package/dist/components/scroll-to-bottom-blob-state.d.ts +40 -0
- package/dist/components/scroll-to-bottom-blob-state.js +109 -0
- package/dist/components/scroll-to-bottom-blob.d.ts +16 -0
- package/dist/components/scroll-to-bottom-blob.js +174 -0
- package/dist/components/select.d.ts +9 -0
- package/dist/components/select.js +12 -0
- package/dist/components/selectable.d.ts +13 -0
- package/dist/components/selectable.js +45 -0
- package/dist/components/send-action.d.ts +33 -0
- package/dist/components/send-action.js +96 -0
- package/dist/components/separator.d.ts +11 -0
- package/dist/components/separator.js +7 -0
- package/dist/components/skeleton.d.ts +5 -0
- package/dist/components/skeleton.js +7 -0
- package/dist/components/slider.d.ts +18 -0
- package/dist/components/slider.js +31 -0
- package/dist/components/stat.d.ts +17 -0
- package/dist/components/stat.js +7 -0
- package/dist/components/status-bead.d.ts +9 -0
- package/dist/components/status-bead.js +107 -0
- package/dist/components/status-mark.d.ts +12 -0
- package/dist/components/status-mark.js +177 -0
- package/dist/components/status.d.ts +38 -0
- package/dist/components/status.js +28 -0
- package/dist/components/store-card.d.ts +21 -0
- package/dist/components/store-card.js +10 -0
- package/dist/components/surface.d.ts +89 -0
- package/dist/components/surface.js +31 -0
- package/dist/components/switch-bead.d.ts +4 -0
- package/dist/components/switch-bead.js +62 -0
- package/dist/components/switch.d.ts +11 -0
- package/dist/components/switch.js +36 -0
- package/dist/components/tabs.d.ts +55 -0
- package/dist/components/tabs.js +81 -0
- package/dist/components/technonomicon.d.ts +192 -0
- package/dist/components/technonomicon.js +121 -0
- package/dist/components/textarea.d.ts +9 -0
- package/dist/components/textarea.js +11 -0
- package/dist/components/toaster.d.ts +4 -0
- package/dist/components/toaster.js +13 -0
- package/dist/components/tool-call-chip.d.ts +22 -0
- package/dist/components/tool-call-chip.js +100 -0
- package/dist/components/tooltip.d.ts +41 -0
- package/dist/components/tooltip.js +169 -0
- package/dist/components/trading-card.d.ts +60 -0
- package/dist/components/trading-card.js +13 -0
- package/dist/components/undo-action.d.ts +52 -0
- package/dist/components/undo-action.js +100 -0
- package/dist/components/undo-toast.d.ts +21 -0
- package/dist/components/undo-toast.js +58 -0
- package/dist/components/visually-hidden.d.ts +9 -0
- package/dist/components/visually-hidden.js +11 -0
- package/dist/components/window.d.ts +65 -0
- package/dist/components/window.js +31 -0
- package/dist/components/wizard.d.ts +17 -0
- package/dist/components/wizard.js +19 -0
- package/dist/enhance/cigarette-card.d.ts +8 -0
- package/dist/enhance/cigarette-card.js +33 -0
- package/dist/enhance/sketch.d.ts +16 -0
- package/dist/enhance/sketch.js +130 -0
- package/dist/form-error.d.ts +7 -0
- package/dist/form-error.js +14 -0
- package/dist/form-fields.d.ts +15 -0
- package/dist/form-fields.js +250 -0
- package/dist/form-schema.d.ts +166 -0
- package/dist/form-schema.js +398 -0
- package/dist/form.d.ts +30 -0
- package/dist/form.js +20 -0
- package/dist/lib/cn.d.ts +8 -0
- package/dist/lib/cn.js +12 -0
- package/dist/lib/format-money.d.ts +2 -0
- package/dist/lib/format-money.js +22 -0
- package/dist/lib/glide.d.ts +12 -0
- package/dist/lib/glide.js +143 -0
- package/dist/lib/ime.d.ts +3 -0
- package/dist/lib/ime.js +8 -0
- package/dist/lib/motion-spring.d.ts +27 -0
- package/dist/lib/motion-spring.js +36 -0
- package/dist/lib/motion.d.ts +51 -0
- package/dist/lib/motion.js +161 -0
- package/dist/lib/reorder-motion.d.ts +13 -0
- package/dist/lib/reorder-motion.js +201 -0
- package/dist/lib/seed-hash.d.ts +6 -0
- package/dist/lib/seed-hash.js +13 -0
- package/dist/lib/tanstack-highlight.d.ts +4 -0
- package/dist/lib/tanstack-highlight.js +41 -0
- package/dist/schema-form.d.ts +120 -0
- package/dist/schema-form.js +129 -0
- package/dist/styles.css +18814 -0
- package/docs/api/README.md +134 -0
- package/docs/api/blocks/app-bar.md +227 -0
- package/docs/api/blocks/app-status-bar.md +257 -0
- package/docs/api/blocks/async-state.md +53 -0
- package/docs/api/blocks/card-grid.md +31 -0
- package/docs/api/blocks/command-menu.md +89 -0
- package/docs/api/blocks/composer.md +40 -0
- package/docs/api/blocks/empty-state.md +97 -0
- package/docs/api/blocks/faq-chat.md +147 -0
- package/docs/api/blocks/game-share-card.md +40 -0
- package/docs/api/blocks/launcher-grid.md +86 -0
- package/docs/api/blocks/listing-detail.md +51 -0
- package/docs/api/blocks/loading-state.md +21 -0
- package/docs/api/blocks/mosaic.md +94 -0
- package/docs/api/blocks/permission-grid.md +79 -0
- package/docs/api/blocks/pricing-card.md +90 -0
- package/docs/api/blocks/release-tag.md +62 -0
- package/docs/api/blocks/resource-select.md +126 -0
- package/docs/api/blocks/result-state.md +42 -0
- package/docs/api/blocks/section-card.md +42 -0
- package/docs/api/blocks/section-heading.md +61 -0
- package/docs/api/blocks/spec-sheet.md +400 -0
- package/docs/api/blocks/status-tray.md +141 -0
- package/docs/api/blocks/upload-queue.md +110 -0
- package/docs/api/components/action-row.md +51 -0
- package/docs/api/components/activity-line.md +103 -0
- package/docs/api/components/alert.md +60 -0
- package/docs/api/components/badge.md +77 -0
- package/docs/api/components/button.md +94 -0
- package/docs/api/components/card.md +165 -0
- package/docs/api/components/chat-bubble.md +66 -0
- package/docs/api/components/checkbox-bead.md +20 -0
- package/docs/api/components/checkbox.md +42 -0
- package/docs/api/components/code-input.md +61 -0
- package/docs/api/components/combobox.md +303 -0
- package/docs/api/components/confirm-action.md +58 -0
- package/docs/api/components/copy-field.md +106 -0
- package/docs/api/components/count-badge.md +39 -0
- package/docs/api/components/cover-pattern.md +43 -0
- package/docs/api/components/data-list.md +80 -0
- package/docs/api/components/dialog.md +235 -0
- package/docs/api/components/diff-view.md +59 -0
- package/docs/api/components/disclosure.md +71 -0
- package/docs/api/components/dropdown-menu.md +269 -0
- package/docs/api/components/field-guide.md +174 -0
- package/docs/api/components/field.md +252 -0
- package/docs/api/components/game-feedback.md +157 -0
- package/docs/api/components/game-hud.md +359 -0
- package/docs/api/components/generation-frame.md +59 -0
- package/docs/api/components/hold-button.md +44 -0
- package/docs/api/components/icon-button.md +56 -0
- package/docs/api/components/icon-checkbox.md +42 -0
- package/docs/api/components/icon-choice-mark.md +31 -0
- package/docs/api/components/icon-lamp.md +92 -0
- package/docs/api/components/icon-radio.md +42 -0
- package/docs/api/components/inline-code.md +21 -0
- package/docs/api/components/input-group.md +53 -0
- package/docs/api/components/input.md +54 -0
- package/docs/api/components/item-grid.md +262 -0
- package/docs/api/components/keycap.md +102 -0
- package/docs/api/components/label.md +40 -0
- package/docs/api/components/listbox.md +131 -0
- package/docs/api/components/loader-blobs.md +9 -0
- package/docs/api/components/loader.md +51 -0
- package/docs/api/components/markdown.md +91 -0
- package/docs/api/components/minimap-labels.md +84 -0
- package/docs/api/components/monogram.md +96 -0
- package/docs/api/components/motion-visibility.md +9 -0
- package/docs/api/components/navigation-rail.md +37 -0
- package/docs/api/components/overlay-root.md +92 -0
- package/docs/api/components/pixel-number.md +120 -0
- package/docs/api/components/popover.md +80 -0
- package/docs/api/components/presence.md +106 -0
- package/docs/api/components/price-tag.md +48 -0
- package/docs/api/components/progress.md +90 -0
- package/docs/api/components/prose.md +48 -0
- package/docs/api/components/quota-meter.md +47 -0
- package/docs/api/components/radio-bead.md +9 -0
- package/docs/api/components/radio-group.md +126 -0
- package/docs/api/components/resize-handle.md +46 -0
- package/docs/api/components/rough-drawing.md +122 -0
- package/docs/api/components/rough.md +66 -0
- package/docs/api/components/scroll-area.md +22 -0
- package/docs/api/components/scroll-motion.md +9 -0
- package/docs/api/components/scroll-to-bottom-blob-state.md +162 -0
- package/docs/api/components/scroll-to-bottom-blob.md +73 -0
- package/docs/api/components/select.md +39 -0
- package/docs/api/components/send-action.md +58 -0
- package/docs/api/components/separator.md +38 -0
- package/docs/api/components/skeleton.md +31 -0
- package/docs/api/components/slider.md +70 -0
- package/docs/api/components/stat.md +42 -0
- package/docs/api/components/status-bead.md +19 -0
- package/docs/api/components/status-mark.md +19 -0
- package/docs/api/components/status.md +105 -0
- package/docs/api/components/store-card.md +53 -0
- package/docs/api/components/surface.md +161 -0
- package/docs/api/components/switch-bead.md +9 -0
- package/docs/api/components/switch.md +40 -0
- package/docs/api/components/tabs.md +138 -0
- package/docs/api/components/technonomicon.md +773 -0
- package/docs/api/components/textarea.md +40 -0
- package/docs/api/components/toaster.md +29 -0
- package/docs/api/components/tool-call-chip.md +68 -0
- package/docs/api/components/tooltip.md +113 -0
- package/docs/api/components/trading-card.md +95 -0
- package/docs/api/components/undo-action.md +117 -0
- package/docs/api/components/undo-toast.md +49 -0
- package/docs/api/components/visually-hidden.md +37 -0
- package/docs/api/components/window.md +291 -0
- package/docs/api/components/wizard.md +65 -0
- package/docs/api/enhance/cigarette-card.md +52 -0
- package/docs/api/enhance/sketch.md +42 -0
- package/docs/api/form.md +1623 -0
- package/docs/api/lib/cn.md +46 -0
- package/docs/api/lib/format-money.md +30 -0
- package/docs/api/lib/glide.md +36 -0
- package/docs/api/lib/ime.md +29 -0
- package/docs/api/lib/motion-spring.md +91 -0
- package/docs/api/lib/motion.md +212 -0
- package/docs/api/lib/reorder-motion.md +46 -0
- package/docs/api/lib/seed-hash.md +31 -0
- package/docs/api/lib/tanstack-highlight.md +40 -0
- package/package.json +597 -3
- package/src/app-status-bar.css +158 -0
- package/src/fonts.css +1 -0
- package/src/status-tray.css +282 -0
- package/src/styles.css +10177 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, } from "react";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { PixelNumber } from "./pixel-number.js";
|
|
5
|
+
/**
|
|
6
|
+
* A click-through overlay for game feedback, placed over the world by the
|
|
7
|
+
* host. Marks inside it are positioned in the layer's own pixels.
|
|
8
|
+
*/
|
|
9
|
+
export const FeedbackLayer = forwardRef(function FeedbackLayer({ paused = false, className, ...props }, ref) {
|
|
10
|
+
return (_jsx("div", { ...props, ref: ref, className: cn("caps-feedback-layer", className), "data-paused": paused }));
|
|
11
|
+
});
|
|
12
|
+
function placement(style, x, y, life) {
|
|
13
|
+
return {
|
|
14
|
+
...style,
|
|
15
|
+
"--caps-feedback-x": `${String(Math.round(x))}px`,
|
|
16
|
+
"--caps-feedback-y": `${String(Math.round(y))}px`,
|
|
17
|
+
"--caps-feedback-life": `${String(life)}ms`,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Gains read gold against a green world; losses always read red. */
|
|
21
|
+
const AMOUNT_TONE = {
|
|
22
|
+
confirm: "success",
|
|
23
|
+
deny: "error",
|
|
24
|
+
hit: "error",
|
|
25
|
+
gain: "warning",
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* One piece of game feedback at a point: a stepped flash ring, an optional
|
|
29
|
+
* floating number and an optional note. Reduced motion keeps the ring and
|
|
30
|
+
* note still and only fades them.
|
|
31
|
+
*/
|
|
32
|
+
export const FeedbackMark = forwardRef(function FeedbackMark({ kind, x, y, label, amount, life = 900, className, style, ...props }, ref) {
|
|
33
|
+
if (![x, y, life].every(Number.isFinite) || life <= 0)
|
|
34
|
+
throw new RangeError("FeedbackMark needs a finite point and life");
|
|
35
|
+
return (_jsxs("div", { ...props, ref: ref, className: cn("caps-feedback-mark", className), "data-kind": kind, style: placement(style, x, y, life), children: [_jsx("span", { className: "caps-feedback-ring", "aria-hidden": "true" }), amount === undefined ? null : (_jsx("span", { className: "caps-feedback-amount", children: _jsx(PixelNumber, { value: amount, signed: true, outline: true, size: "lg", tone: amount < 0 ? "error" : AMOUNT_TONE[kind] }) })), label === undefined ? null : (_jsx("span", { className: "caps-feedback-note", children: label }))] }));
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* One comic sound word at a point: punchy lettering that pops in with a
|
|
39
|
+
* little overshoot, drifts up, and fades. Reduced motion keeps it still at
|
|
40
|
+
* full strength until the host removes it.
|
|
41
|
+
*/
|
|
42
|
+
export const FeedbackWordMark = forwardRef(function FeedbackWordMark({ text, x, y, tone = "impact", size = "m", accent, life = 700, className, style, ...props }, ref) {
|
|
43
|
+
if (![x, y, life].every(Number.isFinite) || life <= 0)
|
|
44
|
+
throw new RangeError("FeedbackWordMark needs a finite point and life");
|
|
45
|
+
if (text.length < 1 || text.length > 12)
|
|
46
|
+
throw new RangeError("FeedbackWordMark text must be 1-12 characters");
|
|
47
|
+
return (_jsxs("div", { ...props, ref: ref, className: cn("caps-feedback-word", className), "data-tone": tone, "data-size": size, "data-accent": accent, style: placement(style, x, y, life), children: [accent === "jaw" ? (_jsxs("span", { className: "caps-feedback-word-jaw", "aria-hidden": "true", children: [_jsx("span", { className: "caps-feedback-word-jaw-top" }), _jsx("span", { className: "caps-feedback-word-jaw-bottom" })] })) : null, _jsx("span", { className: "caps-feedback-word-text", "aria-hidden": "true", children: text })] }));
|
|
48
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { type AnchorHTMLAttributes, type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
/** A region of the game layer, laid over a full-bleed world. */
|
|
3
|
+
export type GameRegionName = "top-left" | "top-right" | "right" | "bottom" | "bottom-left" | "center";
|
|
4
|
+
/** Positions HUD regions over the world; the world stays interactive between them. */
|
|
5
|
+
export declare const GameLayer: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
6
|
+
/** The shell's container-based layout choice. */
|
|
7
|
+
compact?: boolean;
|
|
8
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
9
|
+
/** One named HUD region; only its children take pointer input. */
|
|
10
|
+
export declare const GameRegion: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
11
|
+
region: GameRegionName;
|
|
12
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
13
|
+
/** A dimmed stage for title, pause and end screens; it centres one card. */
|
|
14
|
+
export declare const GameScrim: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
|
|
15
|
+
/** A floating paper plate drawn in the sprites' ink line. */
|
|
16
|
+
export declare const HudPlate: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
17
|
+
density?: "compact" | "roomy";
|
|
18
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
19
|
+
/** Semantic bar tones; the theme supplies each colour. */
|
|
20
|
+
export type HudTone = "success" | "warning" | "error" | "info";
|
|
21
|
+
/** A readable number in the theme's numeric font. */
|
|
22
|
+
export declare function HudNumber({ value, size, }: {
|
|
23
|
+
value: string | number;
|
|
24
|
+
size?: "sm" | "md";
|
|
25
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
/** Props accepted by HudBar. */
|
|
27
|
+
export type HudBarProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
28
|
+
label: string;
|
|
29
|
+
value: number;
|
|
30
|
+
max: number;
|
|
31
|
+
tone?: HudTone;
|
|
32
|
+
glyph?: ReactNode;
|
|
33
|
+
};
|
|
34
|
+
/** A segmented resource bar: health, hunger, noise, hull. */
|
|
35
|
+
export declare const HudBar: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
36
|
+
label: string;
|
|
37
|
+
value: number;
|
|
38
|
+
max: number;
|
|
39
|
+
tone?: HudTone;
|
|
40
|
+
glyph?: ReactNode;
|
|
41
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
42
|
+
/** A counted thing on the HUD: bandages, crowns, round shot. */
|
|
43
|
+
export declare const HudChip: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLSpanElement>, "children"> & {
|
|
44
|
+
label: string;
|
|
45
|
+
value: string | number;
|
|
46
|
+
glyph?: ReactNode;
|
|
47
|
+
} & import("react").RefAttributes<HTMLSpanElement>>;
|
|
48
|
+
/** The light of the day a clock shows. */
|
|
49
|
+
export type HudPhase = "day" | "dusk" | "night" | "dawn";
|
|
50
|
+
/** The game clock: a phase mark, a label, an optional countdown and time. */
|
|
51
|
+
export declare const HudClock: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
52
|
+
label: string;
|
|
53
|
+
detail?: string;
|
|
54
|
+
time?: string;
|
|
55
|
+
phase?: HudPhase;
|
|
56
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
57
|
+
/** How a portrait's subject is doing. */
|
|
58
|
+
export type PortraitMood = "ok" | "warn" | "bad";
|
|
59
|
+
/** A person in the portrait strip: their mark, name and what they are doing. */
|
|
60
|
+
export declare const Portrait: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
61
|
+
name: string;
|
|
62
|
+
status: string;
|
|
63
|
+
mood?: PortraitMood;
|
|
64
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
65
|
+
/** What a minimap mark stands for; the theme colours each kind. */
|
|
66
|
+
export type MinimapMarkKind = "self" | "ally" | "foe" | "place" | "goal";
|
|
67
|
+
/** One mark on the minimap, in whole grid cells. */
|
|
68
|
+
export type MinimapMark = Readonly<{
|
|
69
|
+
x: number;
|
|
70
|
+
y: number;
|
|
71
|
+
kind: MinimapMarkKind;
|
|
72
|
+
label: string;
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* A small chart of the world: a paper grid with named marks. Names are laid
|
|
76
|
+
* out so none overlap (layoutMinimapLabels); one with no room hides and
|
|
77
|
+
* shows on hover or focus.
|
|
78
|
+
*/
|
|
79
|
+
export declare const MinimapPlate: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLElement>, "children"> & {
|
|
80
|
+
label: string;
|
|
81
|
+
columns: number;
|
|
82
|
+
rows: number;
|
|
83
|
+
marks: readonly MinimapMark[];
|
|
84
|
+
} & import("react").RefAttributes<HTMLElement>>;
|
|
85
|
+
/** What a game card is for; each kind sets its size and voice. */
|
|
86
|
+
export type GameCardKind = "goal" | "letter" | "dialogue" | "end" | "screen";
|
|
87
|
+
/** A paper card: goals, letters, dialogue, pause and end screens. */
|
|
88
|
+
export declare const GameCard: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLElement>, "title"> & {
|
|
89
|
+
kind?: GameCardKind;
|
|
90
|
+
kicker?: ReactNode;
|
|
91
|
+
title?: ReactNode;
|
|
92
|
+
footer?: ReactNode;
|
|
93
|
+
} & import("react").RefAttributes<HTMLElement>>;
|
|
94
|
+
/** A ticked or open line on a goal card. */
|
|
95
|
+
export declare function GoalLine({ done, children, }: {
|
|
96
|
+
done: boolean;
|
|
97
|
+
children: ReactNode;
|
|
98
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
99
|
+
/** A row of keycaps: a dock across the bottom or a rail down the side. */
|
|
100
|
+
export declare const HudKeys: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
101
|
+
orientation?: "horizontal" | "vertical";
|
|
102
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
103
|
+
/** A shop or trade line: what, why, how much, and the act of buying it. */
|
|
104
|
+
export declare const OfferRow: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
105
|
+
label: string;
|
|
106
|
+
detail?: string;
|
|
107
|
+
price: string;
|
|
108
|
+
action: ReactNode;
|
|
109
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
110
|
+
/** One ingredient or product of a recipe. */
|
|
111
|
+
export type RecipePart = Readonly<{
|
|
112
|
+
label: string;
|
|
113
|
+
glyph: string;
|
|
114
|
+
have?: number | undefined;
|
|
115
|
+
need?: number | undefined;
|
|
116
|
+
quantity?: number | undefined;
|
|
117
|
+
}>;
|
|
118
|
+
/** A workbench recipe: inputs (short ones marked) become the output. */
|
|
119
|
+
export declare const RecipeRow: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
120
|
+
label: string;
|
|
121
|
+
inputs: readonly RecipePart[];
|
|
122
|
+
output: RecipePart;
|
|
123
|
+
action: ReactNode;
|
|
124
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
125
|
+
/** The quiet credit line on an end card. */
|
|
126
|
+
export declare function MadeWith({ className, ...props }: Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "children">): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Moon, Sun, Sunrise, Sunset } from "lucide-react";
|
|
3
|
+
import { forwardRef, useLayoutEffect, useRef, useState, } from "react";
|
|
4
|
+
import { cn } from "../lib/cn.js";
|
|
5
|
+
import { layoutMinimapLabels } from "./minimap-labels.js";
|
|
6
|
+
import { Monogram } from "./monogram.js";
|
|
7
|
+
function withVariables(style, variables) {
|
|
8
|
+
return { ...style, ...variables };
|
|
9
|
+
}
|
|
10
|
+
/** Positions HUD regions over the world; the world stays interactive between them. */
|
|
11
|
+
export const GameLayer = forwardRef(function GameLayer({ className, compact = false, ...props }, ref) {
|
|
12
|
+
return (_jsx("div", { ...props, ref: ref, "data-compact": compact || undefined, className: cn("caps-game-layer", className) }));
|
|
13
|
+
});
|
|
14
|
+
/** One named HUD region; only its children take pointer input. */
|
|
15
|
+
export const GameRegion = forwardRef(function GameRegion({ region, className, ...props }, ref) {
|
|
16
|
+
return (_jsx("div", { ...props, ref: ref, className: cn("caps-game-region", className), "data-region": region }));
|
|
17
|
+
});
|
|
18
|
+
/** A dimmed stage for title, pause and end screens; it centres one card. */
|
|
19
|
+
export const GameScrim = forwardRef(function GameScrim({ className, ...props }, ref) {
|
|
20
|
+
return (_jsx("div", { ...props, ref: ref, className: cn("caps-game-scrim", className) }));
|
|
21
|
+
});
|
|
22
|
+
/** A floating paper plate drawn in the sprites' ink line. */
|
|
23
|
+
export const HudPlate = forwardRef(function HudPlate({ density = "compact", className, ...props }, ref) {
|
|
24
|
+
return (_jsx("div", { ...props, ref: ref, className: cn("caps-hud-plate", className), "data-density": density }));
|
|
25
|
+
});
|
|
26
|
+
/** A single short glyph (an emoji or a letter) shown before a label. */
|
|
27
|
+
function Glyph({ glyph }) {
|
|
28
|
+
return glyph ? (_jsx("span", { className: "caps-hud-glyph", "aria-hidden": "true", children: glyph })) : null;
|
|
29
|
+
}
|
|
30
|
+
/** A readable number in the theme's numeric font. */
|
|
31
|
+
export function HudNumber({ value, size = "sm", }) {
|
|
32
|
+
return (_jsx("span", { className: "caps-hud-text-value caps-numeric", "data-size": size, children: String(value) }));
|
|
33
|
+
}
|
|
34
|
+
/** A segmented resource bar: health, hunger, noise, hull. */
|
|
35
|
+
export const HudBar = forwardRef(function HudBar({ label, value, max, tone = "info", glyph, className, style, ...props }, ref) {
|
|
36
|
+
const share = max > 0 ? Math.min(1, Math.max(0, value / max)) : 0;
|
|
37
|
+
return (_jsxs("div", { ...props, ref: ref, className: cn("caps-hud-bar", className), "data-tone": tone, "data-low": share <= 0.25 ? "" : undefined, style: withVariables(style, { "--caps-hud-share": share }), children: [_jsx(Glyph, { glyph: glyph }), _jsx("span", { className: "caps-hud-bar-label", children: label }), _jsx("span", { className: "caps-hud-bar-value", children: _jsx(HudNumber, { value: `${String(value)} / ${String(max)}` }) }), _jsx("span", { className: "caps-hud-bar-track", role: "meter", "aria-label": label, "aria-valuemin": 0, "aria-valuemax": max, "aria-valuenow": value, children: _jsx("span", { className: "caps-hud-bar-fill" }) })] }));
|
|
38
|
+
});
|
|
39
|
+
/** A counted thing on the HUD: bandages, crowns, round shot. */
|
|
40
|
+
export const HudChip = forwardRef(function HudChip({ label, value, glyph, className, ...props }, ref) {
|
|
41
|
+
return (_jsxs("span", { ...props, ref: ref, className: cn("caps-hud-chip", className), children: [_jsx(Glyph, { glyph: glyph }), _jsx("span", { className: "caps-hud-chip-label", children: label }), " ", _jsx("span", { className: "caps-hud-chip-value", children: _jsx(HudNumber, { value: value }) })] }));
|
|
42
|
+
});
|
|
43
|
+
const PHASE_ICON = { day: Sun, dusk: Sunset, night: Moon, dawn: Sunrise };
|
|
44
|
+
/** The game clock: a phase mark, a label, an optional countdown and time. */
|
|
45
|
+
export const HudClock = forwardRef(function HudClock({ label, detail, time, phase = "day", className, ...props }, ref) {
|
|
46
|
+
const Icon = PHASE_ICON[phase];
|
|
47
|
+
return (_jsxs("div", { ...props, ref: ref, className: cn("caps-hud-clock", className), "data-phase": phase, children: [_jsx("span", { className: "caps-hud-clock-mark", "aria-hidden": "true", children: _jsx(Icon, {}) }), _jsxs("span", { className: "caps-hud-clock-text", children: [_jsx("span", { className: "caps-hud-clock-label", children: label }), detail ? (_jsx("span", { className: "caps-hud-clock-detail", children: detail })) : null] }), time ? (_jsx("span", { className: "caps-hud-clock-time", children: _jsx(HudNumber, { value: time, size: "md" }) })) : null] }));
|
|
48
|
+
});
|
|
49
|
+
/** A person in the portrait strip: their mark, name and what they are doing. */
|
|
50
|
+
export const Portrait = forwardRef(function Portrait({ name, status, mood = "ok", className, ...props }, ref) {
|
|
51
|
+
return (_jsxs("div", { ...props, ref: ref, className: cn("caps-portrait", className), "data-mood": mood, children: [_jsx(Monogram, { name: name, size: "lg", className: "caps-portrait-avatar" }), _jsxs("span", { className: "caps-portrait-text", children: [_jsx("span", { className: "caps-portrait-name", children: name }), _jsx("span", { className: "caps-portrait-status", children: status })] })] }));
|
|
52
|
+
});
|
|
53
|
+
/** Label metrics at the chart's name size (0.625rem, weight 800). */
|
|
54
|
+
const MINIMAP_LABEL = {
|
|
55
|
+
charWidth: 6.2,
|
|
56
|
+
lineHeight: 13,
|
|
57
|
+
padding: 2,
|
|
58
|
+
dot: 10,
|
|
59
|
+
gap: 2,
|
|
60
|
+
};
|
|
61
|
+
/** The chart's width before it is measured (15rem). */
|
|
62
|
+
const MINIMAP_WIDTH = 240;
|
|
63
|
+
/**
|
|
64
|
+
* A small chart of the world: a paper grid with named marks. Names are laid
|
|
65
|
+
* out so none overlap (layoutMinimapLabels); one with no room hides and
|
|
66
|
+
* shows on hover or focus.
|
|
67
|
+
*/
|
|
68
|
+
export const MinimapPlate = forwardRef(function MinimapPlate({ label, columns, rows, marks, className, style, ...props }, ref) {
|
|
69
|
+
const field = useRef(null);
|
|
70
|
+
const [width, setWidth] = useState(MINIMAP_WIDTH);
|
|
71
|
+
useLayoutEffect(() => {
|
|
72
|
+
const element = field.current;
|
|
73
|
+
if (!element)
|
|
74
|
+
return;
|
|
75
|
+
const measure = () => {
|
|
76
|
+
if (element.clientWidth > 0)
|
|
77
|
+
setWidth(element.clientWidth);
|
|
78
|
+
};
|
|
79
|
+
measure();
|
|
80
|
+
// Test DOMs (jsdom) have no ResizeObserver; the first measure stands.
|
|
81
|
+
if (!("ResizeObserver" in globalThis))
|
|
82
|
+
return;
|
|
83
|
+
const observer = new ResizeObserver(measure);
|
|
84
|
+
observer.observe(element);
|
|
85
|
+
return () => {
|
|
86
|
+
observer.disconnect();
|
|
87
|
+
};
|
|
88
|
+
}, []);
|
|
89
|
+
const sides = layoutMinimapLabels(marks.map((mark) => ({
|
|
90
|
+
x: (mark.x + 0.5) / columns,
|
|
91
|
+
y: (mark.y + 0.5) / rows,
|
|
92
|
+
named: mark.kind === "place" || mark.kind === "goal",
|
|
93
|
+
solid: mark.kind !== "foe" && mark.kind !== "ally",
|
|
94
|
+
goal: mark.kind === "goal",
|
|
95
|
+
label: mark.label,
|
|
96
|
+
})), { ...MINIMAP_LABEL, width, height: (width * rows) / columns });
|
|
97
|
+
return (_jsxs("figure", { ...props, ref: ref, className: cn("caps-minimap", className), style: withVariables(style, {
|
|
98
|
+
"--caps-minimap-columns": columns,
|
|
99
|
+
"--caps-minimap-rows": rows,
|
|
100
|
+
}), children: [_jsx("div", { ref: field, className: "caps-minimap-field", role: "img", "aria-label": `${label}: ${marks.map((mark) => mark.label).join(", ")}`, children: marks.map((mark, index) => (_jsx("span", { className: "caps-minimap-mark", "data-kind": mark.kind, "data-label": mark.kind === "place" || mark.kind === "goal"
|
|
101
|
+
? sides[index]
|
|
102
|
+
: undefined, title: mark.label, style: withVariables(undefined, {
|
|
103
|
+
"--caps-minimap-x": (mark.x + 0.5) / columns,
|
|
104
|
+
"--caps-minimap-y": (mark.y + 0.5) / rows,
|
|
105
|
+
}), children: mark.kind === "place" || mark.kind === "goal" ? (_jsx("span", { className: "caps-minimap-name", "aria-hidden": "true", children: mark.label })) : null }, `${mark.kind}:${mark.label}`))) }), _jsx("figcaption", { className: "caps-minimap-caption", children: label })] }));
|
|
106
|
+
});
|
|
107
|
+
/** A paper card: goals, letters, dialogue, pause and end screens. */
|
|
108
|
+
export const GameCard = forwardRef(function GameCard({ kind = "goal", kicker, title, footer, className, children, ...props }, ref) {
|
|
109
|
+
return (_jsxs("article", { ...props, ref: ref, className: cn("caps-hud-card", className), "data-kind": kind, children: [kicker || title ? (_jsxs("header", { className: "caps-hud-card-header", children: [kicker ? (_jsx("span", { className: "caps-hud-card-kicker", children: kicker })) : null, title ? _jsx("h2", { className: "caps-hud-card-title", children: title }) : null] })) : null, _jsx("div", { className: "caps-hud-card-body", children: children }), footer ? (_jsx("footer", { className: "caps-hud-card-footer", children: footer })) : null] }));
|
|
110
|
+
});
|
|
111
|
+
/** A ticked or open line on a goal card. */
|
|
112
|
+
export function GoalLine({ done, children, }) {
|
|
113
|
+
return (_jsxs("p", { className: "caps-goal-line", "data-done": done, children: [_jsx("span", { className: "caps-goal-box", "aria-hidden": "true" }), _jsx("span", { className: "caps-goal-text", children: children }), _jsx("span", { className: "sr-only", children: done ? " (done)" : " (to do)" })] }));
|
|
114
|
+
}
|
|
115
|
+
/** A row of keycaps: a dock across the bottom or a rail down the side. */
|
|
116
|
+
export const HudKeys = forwardRef(function HudKeys({ orientation = "horizontal", className, ...props }, ref) {
|
|
117
|
+
return (_jsx("div", { role: "toolbar", "aria-orientation": orientation, ...props, ref: ref, className: cn("caps-hud-keys", className), "data-orientation": orientation }));
|
|
118
|
+
});
|
|
119
|
+
/** A shop or trade line: what, why, how much, and the act of buying it. */
|
|
120
|
+
export const OfferRow = forwardRef(function OfferRow({ label, detail, price, action, className, ...props }, ref) {
|
|
121
|
+
return (_jsxs("div", { ...props, ref: ref, className: cn("caps-offer-row", className), children: [_jsxs("span", { className: "caps-offer-text", children: [_jsx("span", { className: "caps-offer-label", children: label }), detail ? _jsx("span", { className: "caps-offer-detail", children: detail }) : null] }), _jsx("span", { className: "caps-offer-price", children: price }), _jsx("span", { className: "caps-offer-action", children: action })] }));
|
|
122
|
+
});
|
|
123
|
+
function RecipeSlot({ part }) {
|
|
124
|
+
const short = part.need !== undefined && part.have !== undefined && part.have < part.need;
|
|
125
|
+
return (_jsxs("span", { className: "caps-recipe-slot", "data-short": short ? "" : undefined, title: part.label, children: [_jsx("span", { className: "caps-recipe-glyph", "aria-hidden": "true", children: part.glyph }), _jsx("span", { className: "caps-recipe-count", children: part.need !== undefined
|
|
126
|
+
? `${String(part.have ?? 0)}/${String(part.need)}`
|
|
127
|
+
: `×${String(part.quantity ?? 1)}` }), _jsx("span", { className: "sr-only", children: part.label })] }));
|
|
128
|
+
}
|
|
129
|
+
/** A workbench recipe: inputs (short ones marked) become the output. */
|
|
130
|
+
export const RecipeRow = forwardRef(function RecipeRow({ label, inputs, output, action, className, ...props }, ref) {
|
|
131
|
+
return (_jsxs("div", { ...props, ref: ref, className: cn("caps-recipe-row", className), children: [_jsx("span", { className: "caps-recipe-label", children: label }), _jsxs("span", { className: "caps-recipe-parts", children: [inputs.map((part) => (_jsx(RecipeSlot, { part: part }, part.label))), _jsx("span", { className: "caps-recipe-arrow", "aria-hidden": "true", children: "\u2192" }), _jsx(RecipeSlot, { part: output })] }), _jsx("span", { className: "caps-recipe-action", children: action })] }));
|
|
132
|
+
});
|
|
133
|
+
/** The quiet credit line on an end card. */
|
|
134
|
+
export function MadeWith({ className, ...props }) {
|
|
135
|
+
return (_jsxs("a", { ...props, className: cn("caps-made-with", className), children: ["Made with ", _jsx("strong", { children: "Fungi" }), " \u00B7 make your own"] }));
|
|
136
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
/** Where one generated piece of media is in its life. */
|
|
3
|
+
export type GenerationStatus = "queued" | "generating" | "refining" | "done" | "failed";
|
|
4
|
+
/** Props accepted by GenerationFrame. */
|
|
5
|
+
export type GenerationFrameProps = {
|
|
6
|
+
status: GenerationStatus;
|
|
7
|
+
/** The finished media (or, while refining, a rough preview of it). */
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
/** Width over height. Omit when the parent already sizes the frame. */
|
|
10
|
+
aspectRatio?: number;
|
|
11
|
+
/** What is happening, for people using a screen reader. */
|
|
12
|
+
label?: ReactNode;
|
|
13
|
+
/** The working indicator; Caps' bloom loader by default. */
|
|
14
|
+
indicator?: ReactNode;
|
|
15
|
+
/** Shown over the dimmed frame when generation fails. */
|
|
16
|
+
failure?: ReactNode;
|
|
17
|
+
} & Omit<HTMLAttributes<HTMLDivElement>, "children">;
|
|
18
|
+
/**
|
|
19
|
+
* One fixed frame for a generation, from queued to done: the frame keeps
|
|
20
|
+
* its size the whole time, so nothing around it moves. The finished media
|
|
21
|
+
* develops inside it (it sharpens out of a soft blur), and a failure dims the
|
|
22
|
+
* frame behind its message. Reduced motion keeps short fades only.
|
|
23
|
+
*/
|
|
24
|
+
export declare const GenerationFrame: import("react").ForwardRefExoticComponent<{
|
|
25
|
+
status: GenerationStatus;
|
|
26
|
+
/** The finished media (or, while refining, a rough preview of it). */
|
|
27
|
+
children?: ReactNode;
|
|
28
|
+
/** Width over height. Omit when the parent already sizes the frame. */
|
|
29
|
+
aspectRatio?: number;
|
|
30
|
+
/** What is happening, for people using a screen reader. */
|
|
31
|
+
label?: ReactNode;
|
|
32
|
+
/** The working indicator; Caps' bloom loader by default. */
|
|
33
|
+
indicator?: ReactNode;
|
|
34
|
+
/** Shown over the dimmed frame when generation fails. */
|
|
35
|
+
failure?: ReactNode;
|
|
36
|
+
} & Omit<HTMLAttributes<HTMLDivElement>, "children"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useImperativeHandle, useLayoutEffect, useRef, useState, } from "react";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { canAnimate, motionEasing, readMotion } from "../lib/motion.js";
|
|
5
|
+
import { Loader } from "./loader.js";
|
|
6
|
+
import { VisuallyHidden } from "./visually-hidden.js";
|
|
7
|
+
const defaultLabels = {
|
|
8
|
+
queued: "Waiting to start",
|
|
9
|
+
generating: "Generating",
|
|
10
|
+
refining: "Refining",
|
|
11
|
+
done: undefined,
|
|
12
|
+
failed: undefined,
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* One fixed frame for a generation, from queued to done: the frame keeps
|
|
16
|
+
* its size the whole time, so nothing around it moves. The finished media
|
|
17
|
+
* develops inside it (it sharpens out of a soft blur), and a failure dims the
|
|
18
|
+
* frame behind its message. Reduced motion keeps short fades only.
|
|
19
|
+
*/
|
|
20
|
+
export const GenerationFrame = forwardRef(function GenerationFrame({ status, children, aspectRatio, label, indicator, failure, className, style, ...props }, ref) {
|
|
21
|
+
const node = useRef(null);
|
|
22
|
+
useImperativeHandle(ref, () => node.current);
|
|
23
|
+
const previous = useRef(status);
|
|
24
|
+
// The working layer stays under the picture until the picture has
|
|
25
|
+
// developed over it, so the loader never blinks out first.
|
|
26
|
+
const [developing, setDeveloping] = useState(false);
|
|
27
|
+
const [shownStatus, setShownStatus] = useState(status);
|
|
28
|
+
if (shownStatus !== status) {
|
|
29
|
+
setShownStatus(status);
|
|
30
|
+
setDeveloping(status === "done");
|
|
31
|
+
}
|
|
32
|
+
useLayoutEffect(() => {
|
|
33
|
+
const element = node.current;
|
|
34
|
+
const before = previous.current;
|
|
35
|
+
previous.current = status;
|
|
36
|
+
if (!element || before === status)
|
|
37
|
+
return;
|
|
38
|
+
if (!canAnimate(element)) {
|
|
39
|
+
setDeveloping(false);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const motion = readMotion(element);
|
|
43
|
+
const easing = motionEasing(motion, motion.ease);
|
|
44
|
+
const animations = [];
|
|
45
|
+
const media = element.querySelector(':scope > [data-layer="media"]');
|
|
46
|
+
const placeholder = element.querySelector(':scope > [data-layer="placeholder"]');
|
|
47
|
+
const note = element.querySelector(':scope > [data-layer="failure"]');
|
|
48
|
+
if (status === "done" && media) {
|
|
49
|
+
// The picture develops: it sharpens and settles out of a soft blur.
|
|
50
|
+
const develop = media.animate(motion.reduced
|
|
51
|
+
? [{ opacity: 0 }, { opacity: 1 }]
|
|
52
|
+
: [
|
|
53
|
+
{ opacity: 0, filter: "blur(10px)", transform: "scale(1.03)" },
|
|
54
|
+
{ opacity: 1, filter: "blur(0px)", transform: "none" },
|
|
55
|
+
], {
|
|
56
|
+
duration: motion.reduced ? motion.fast : motion.slow * 2,
|
|
57
|
+
easing,
|
|
58
|
+
fill: "backwards",
|
|
59
|
+
});
|
|
60
|
+
develop.addEventListener("finish", () => {
|
|
61
|
+
setDeveloping(false);
|
|
62
|
+
});
|
|
63
|
+
animations.push(develop);
|
|
64
|
+
if (placeholder)
|
|
65
|
+
animations.push(placeholder.animate([{ opacity: 1 }, { opacity: 0 }], {
|
|
66
|
+
duration: motion.reduced ? motion.fast : motion.slow,
|
|
67
|
+
easing,
|
|
68
|
+
fill: "forwards",
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
if (status === "failed" && note)
|
|
72
|
+
animations.push(note.animate([
|
|
73
|
+
{
|
|
74
|
+
opacity: 0,
|
|
75
|
+
transform: `translateY(${String(motion.distance)}px)`,
|
|
76
|
+
},
|
|
77
|
+
{ opacity: 1, transform: "none" },
|
|
78
|
+
], { duration: motion.normal, easing }));
|
|
79
|
+
if (before === "failed" && placeholder)
|
|
80
|
+
animations.push(placeholder.animate([{ opacity: 0.4 }, { opacity: 1 }], {
|
|
81
|
+
duration: motion.normal,
|
|
82
|
+
easing,
|
|
83
|
+
}));
|
|
84
|
+
return () => {
|
|
85
|
+
for (const animation of animations)
|
|
86
|
+
animation.cancel();
|
|
87
|
+
};
|
|
88
|
+
}, [status]);
|
|
89
|
+
const working = status === "queued" || status === "generating" || status === "refining";
|
|
90
|
+
const spoken = label ?? defaultLabels[status];
|
|
91
|
+
const showsMedia = status === "done" || status === "refining";
|
|
92
|
+
return (_jsxs("div", { ...props, ref: node, className: cn("caps-generation-frame", className), "data-status": status, "data-aspect": aspectRatio === undefined ? undefined : "", "aria-busy": working, style: aspectRatio === undefined ? style : { aspectRatio, ...style }, children: [status === "done" && !developing ? null : (_jsxs("div", { className: cn("caps-generation-placeholder", status === "queued" && "skeleton"), "data-layer": "placeholder", role: status === "done" ? undefined : "status", "aria-hidden": status === "done" ? true : undefined, children: [status === "generating" ||
|
|
93
|
+
status === "refining" ||
|
|
94
|
+
status === "done"
|
|
95
|
+
? (indicator ?? _jsx(Loader, { decorative: true, size: "lg", variant: "bloom" }))
|
|
96
|
+
: null, spoken === undefined ? null : (_jsx(VisuallyHidden, { children: spoken }))] })), showsMedia ? (_jsx("div", { className: "caps-generation-media", "data-layer": "media", children: children })) : null, status === "failed" && failure !== undefined ? (_jsx("div", { className: "caps-generation-failure", "data-layer": "failure", role: "alert", children: failure })) : null] }));
|
|
97
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** @internal The eight-unit bead's material, positioned with an SVG transform. */
|
|
2
|
+
export declare const GooeySurfaceDefinition: import("react").ForwardRefExoticComponent<{
|
|
3
|
+
id: string;
|
|
4
|
+
transform: string;
|
|
5
|
+
} & import("react").RefAttributes<SVGRadialGradientElement>>;
|
|
6
|
+
/** @internal Cast one themed shadow from the finished silhouette. */
|
|
7
|
+
export declare function GooeyShadowDefinition({ id }: {
|
|
8
|
+
id: string;
|
|
9
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
/** @internal Opposing inner shadows shared by recessed control wells. */
|
|
11
|
+
export declare function GooeyInsetDefinition({ id }: {
|
|
12
|
+
id: string;
|
|
13
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
/** @internal Props shared by Caps' private gooey filter definitions. */
|
|
15
|
+
export type GooeyFilterDefinitionProps = {
|
|
16
|
+
values?: GooeyFilterValues;
|
|
17
|
+
id: string;
|
|
18
|
+
};
|
|
19
|
+
/** @internal Numeric values for one SVG gooey filter profile. */
|
|
20
|
+
export type GooeyFilterValues = Readonly<{
|
|
21
|
+
alphaIntercept: number;
|
|
22
|
+
alphaSlope: number;
|
|
23
|
+
blurStdDeviation: number;
|
|
24
|
+
}>;
|
|
25
|
+
/** @internal Filter calibrated for Status's eight-unit bead. */
|
|
26
|
+
export declare const STATUS_GOOEY_FILTER: Readonly<{
|
|
27
|
+
alphaIntercept: -8;
|
|
28
|
+
alphaSlope: 18;
|
|
29
|
+
blurStdDeviation: 2;
|
|
30
|
+
}>;
|
|
31
|
+
/** @internal The single SVG filter definition shared by Caps' gooey family. */
|
|
32
|
+
export declare function GooeyFilterDefinition({ id, values, }: GooeyFilterDefinitionProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { STIPE_GOOEY_FILTER } from "@fungi.computer/stipe";
|
|
3
|
+
import { forwardRef } from "react";
|
|
4
|
+
/** @internal The eight-unit bead's material, positioned with an SVG transform. */
|
|
5
|
+
export const GooeySurfaceDefinition = forwardRef(function GooeySurfaceDefinition({ id, transform }, ref) {
|
|
6
|
+
return (_jsxs("radialGradient", { ref: ref, id: id, gradientUnits: "userSpaceOnUse", cx: "-1.2", cy: "-1.6", r: "5.6", gradientTransform: transform, children: [_jsx("stop", { offset: "0", stopColor: "color-mix(in oklab, var(--color-white) calc(var(--depth) * 50%), currentColor)" }), _jsx("stop", { offset: "1", stopColor: "currentColor" })] }));
|
|
7
|
+
});
|
|
8
|
+
/** @internal Cast one themed shadow from the finished silhouette. */
|
|
9
|
+
export function GooeyShadowDefinition({ id }) {
|
|
10
|
+
return (_jsx("filter", { id: id, x: "-50%", y: "-50%", width: "200%", height: "200%", children: _jsx("feDropShadow", { dx: "0", dy: "1.5", stdDeviation: "1", floodColor: "currentColor", style: { floodOpacity: "calc(var(--depth) * 0.6)" } }) }));
|
|
11
|
+
}
|
|
12
|
+
/** @internal Opposing inner shadows shared by recessed control wells. */
|
|
13
|
+
export function GooeyInsetDefinition({ id }) {
|
|
14
|
+
return (_jsxs("filter", { id: id, x: "-10%", y: "-10%", width: "120%", height: "120%", colorInterpolationFilters: "sRGB", children: [_jsx("feGaussianBlur", { in: "SourceAlpha", stdDeviation: "0.3", result: "soft-edge" }), _jsx("feOffset", { in: "soft-edge", dx: "0.25", dy: "0.45", result: "lower-edge" }), _jsx("feComposite", { in: "SourceAlpha", in2: "lower-edge", operator: "out", result: "inner-shadow" }), _jsx("feFlood", { floodColor: "var(--color-black)", floodOpacity: "calc(var(--depth) * 0.28)" }), _jsx("feComposite", { in2: "inner-shadow", operator: "in", result: "shade" }), _jsx("feOffset", { in: "soft-edge", dx: "-0.2", dy: "-0.35", result: "upper-edge" }), _jsx("feComposite", { in: "SourceAlpha", in2: "upper-edge", operator: "out", result: "inner-highlight" }), _jsx("feFlood", { floodColor: "var(--color-white)", floodOpacity: "calc(var(--depth) * 0.7)" }), _jsx("feComposite", { in2: "inner-highlight", operator: "in", result: "highlight" }), _jsxs("feMerge", { children: [_jsx("feMergeNode", { in: "SourceGraphic" }), _jsx("feMergeNode", { in: "shade" }), _jsx("feMergeNode", { in: "highlight" })] })] }));
|
|
15
|
+
}
|
|
16
|
+
/** @internal Filter calibrated for Status's eight-unit bead. */
|
|
17
|
+
export const STATUS_GOOEY_FILTER = Object.freeze({
|
|
18
|
+
alphaIntercept: -8,
|
|
19
|
+
alphaSlope: 18,
|
|
20
|
+
blurStdDeviation: 2,
|
|
21
|
+
});
|
|
22
|
+
/** @internal The single SVG filter definition shared by Caps' gooey family. */
|
|
23
|
+
export function GooeyFilterDefinition({ id, values = STIPE_GOOEY_FILTER, }) {
|
|
24
|
+
return (_jsxs("filter", { id: id, x: "-50%", y: "-50%", width: "200%", height: "200%", children: [_jsx("feGaussianBlur", { in: "SourceGraphic", stdDeviation: values.blurStdDeviation, result: "blur" }), _jsx("feColorMatrix", { in: "blur", values: `1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 ${String(values.alphaSlope)} ${String(values.alphaIntercept)}`, result: "gooey" }), _jsx("feComposite", { in: "SourceGraphic", in2: "gooey", operator: "atop" })] }));
|
|
25
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type ButtonProps } from "./button.js";
|
|
3
|
+
/** Props accepted by HoldButton. */
|
|
4
|
+
export type HoldButtonProps = {
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
/** Runs once the button has been held for `holdMs`. */
|
|
7
|
+
onConfirm: () => void;
|
|
8
|
+
/** How long to hold, in milliseconds: a deliberate pause, not motion. */
|
|
9
|
+
holdMs?: number;
|
|
10
|
+
/** Spoken description of the gesture. */
|
|
11
|
+
holdHint?: string;
|
|
12
|
+
} & Omit<ButtonProps, "children" | "onClick">;
|
|
13
|
+
/**
|
|
14
|
+
* For an action nothing can undo: press and hold (pointer, Space or Enter)
|
|
15
|
+
* while a fill rises; let go early and it drops back; hold to the top and the
|
|
16
|
+
* action runs. Under reduced motion the fill rises in three steps.
|
|
17
|
+
*/
|
|
18
|
+
export declare const HoldButton: import("react").ForwardRefExoticComponent<{
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
/** Runs once the button has been held for `holdMs`. */
|
|
21
|
+
onConfirm: () => void;
|
|
22
|
+
/** How long to hold, in milliseconds: a deliberate pause, not motion. */
|
|
23
|
+
holdMs?: number;
|
|
24
|
+
/** Spoken description of the gesture. */
|
|
25
|
+
holdHint?: string;
|
|
26
|
+
} & Omit<ButtonProps, "children" | "onClick"> & import("react").RefAttributes<HTMLButtonElement>>;
|