@ctrl-ui/react 0.8.7 → 0.8.9
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/dist/action-bar.d.ts +15 -7
- package/dist/action-bar.js +39 -19
- package/dist/activity.js +7 -15
- package/dist/audio-recorder.d.ts +14 -4
- package/dist/audio-recorder.js +82 -29
- package/dist/audio-visualizer.js +1 -1
- package/dist/blocks/coding-agent.js +39 -39
- package/dist/blocks/design-canvas.d.ts +5 -1
- package/dist/blocks/design-canvas.js +198 -92
- package/dist/blocks/discussion.d.ts +28 -0
- package/dist/blocks/discussion.js +67 -0
- package/dist/blocks/file-explorer-data.d.ts +1 -0
- package/dist/blocks/file-explorer-data.js +4 -1
- package/dist/blocks/file-explorer.d.ts +9 -1
- package/dist/blocks/file-explorer.js +461 -192
- package/dist/blocks/settings.d.ts +2 -1
- package/dist/blocks/settings.js +101 -79
- package/dist/blocks/team-chat.d.ts +12 -0
- package/dist/blocks/team-chat.js +26 -0
- package/dist/blocks/theme-toggle.js +23 -31
- package/dist/chat-composer-attachment.d.ts +4 -1
- package/dist/chat-composer-attachment.js +35 -10
- package/dist/chat-composer-editor/extensions/mention.d.ts +5 -1
- package/dist/chat-composer-editor/extensions/mention.js +59 -28
- package/dist/chat-composer-editor/ghost.js +2 -4
- package/dist/chat-composer-editor/types.d.ts +5 -0
- package/dist/chat-composer-editor.d.ts +1 -1
- package/dist/chat-composer-editor.js +57 -30
- package/dist/chat-composer.d.ts +13 -32
- package/dist/chat-composer.js +52 -12
- package/dist/chat-layout.d.ts +19 -0
- package/dist/chat-layout.js +91 -40
- package/dist/chat-message/social.d.ts +26 -0
- package/dist/chat-message/social.js +122 -0
- package/dist/chat-message.d.ts +28 -29
- package/dist/chat-message.js +106 -25
- package/dist/context.d.ts +2 -2
- package/dist/context.js +38 -23
- package/dist/dynamic-notification-canvas-loop.d.ts +24 -0
- package/dist/dynamic-notification-canvas-loop.js +113 -0
- package/dist/dynamic-notification-glass.js +23 -114
- package/dist/dynamic-notification-liquid.js +14 -118
- package/dist/dynamic-notification.d.ts +16 -8
- package/dist/dynamic-notification.js +85 -22
- package/dist/email/email-brand.d.ts +7 -4
- package/dist/email/email-brand.js +9 -6
- package/dist/email/email-code.d.ts +3 -2
- package/dist/email/email-code.js +11 -9
- package/dist/email/email-footer.d.ts +5 -3
- package/dist/email/email-footer.js +11 -5
- package/dist/email/email.d.ts +31 -9
- package/dist/email/email.js +50 -26
- package/dist/email/templates.d.ts +10 -9
- package/dist/email/templates.js +102 -38
- package/dist/email/theme.js +1 -1
- package/dist/environment-variables.d.ts +4 -2
- package/dist/environment-variables.js +52 -32
- package/dist/filter-bar/editor.js +20 -8
- package/dist/filter-bar/model.d.ts +1 -0
- package/dist/filter-bar/model.js +10 -1
- package/dist/filter-bar/motion.js +4 -4
- package/dist/filter-bar/parts.js +19 -8
- package/dist/filter-bar/types.d.ts +1 -0
- package/dist/filter-bar.js +19 -13
- package/dist/hooks/use-audio-recorder.js +12 -6
- package/dist/hooks/use-chat-composer.d.ts +19 -7
- package/dist/hooks/use-chat-composer.js +32 -5
- package/dist/hooks/use-chat-message.d.ts +28 -2
- package/dist/hooks/use-chat-message.js +41 -10
- package/dist/hooks/use-chat-thread-scroll.js +43 -7
- package/dist/hooks/use-color-area.d.ts +3 -3
- package/dist/hooks/use-color-area.js +13 -12
- package/dist/hooks/use-copy-to-clipboard.d.ts +2 -1
- package/dist/hooks/use-copy-to-clipboard.js +20 -20
- package/dist/hooks/use-dynamic-notification.d.ts +6 -2
- package/dist/hooks/use-dynamic-notification.js +26 -10
- package/dist/hooks/use-mobile.d.ts +3 -2
- package/dist/hooks/use-mobile.js +8 -4
- package/dist/hooks/use-textarea-trigger-menu.js +2 -1
- package/dist/hooks/use-trigger-menu.d.ts +54 -0
- package/dist/hooks/use-trigger-menu.js +102 -47
- package/dist/hooks/use-user-ask.d.ts +2 -0
- package/dist/hooks/use-user-ask.js +35 -41
- package/dist/inline-attachment.d.ts +6 -2
- package/dist/inline-attachment.js +36 -15
- package/dist/inline-citation.d.ts +12 -5
- package/dist/inline-citation.js +25 -16
- package/dist/knob-contracts/audio-recorder-knobs.d.ts +1 -1
- package/dist/knob-contracts/audio-recorder-knobs.js +2 -1
- package/dist/knob-contracts/audio-visualizer-knobs.d.ts +1 -1
- package/dist/knob-contracts/audio-visualizer-knobs.js +2 -1
- package/dist/knob-contracts/avatar-knobs.d.ts +1 -1
- package/dist/knob-contracts/avatar-knobs.js +6 -1
- package/dist/knob-contracts/badge-knobs.d.ts +1 -1
- package/dist/knob-contracts/badge-knobs.js +2 -1
- package/dist/knob-contracts/breadcrumb-knobs.d.ts +4 -0
- package/dist/knob-contracts/breadcrumb-knobs.js +8 -0
- package/dist/knob-contracts/button-knobs.d.ts +1 -1
- package/dist/knob-contracts/button-knobs.js +1 -0
- package/dist/knob-contracts/chat-message-knobs.d.ts +1 -1
- package/dist/knob-contracts/chat-message-knobs.js +8 -1
- package/dist/knob-contracts/code-diff-knobs.d.ts +1 -1
- package/dist/knob-contracts/code-diff-knobs.js +2 -0
- package/dist/knob-contracts/code-knobs.d.ts +1 -1
- package/dist/knob-contracts/code-knobs.js +2 -1
- package/dist/knob-contracts/dynamic-notification-knobs.d.ts +1 -1
- package/dist/knob-contracts/dynamic-notification-knobs.js +2 -0
- package/dist/knob-contracts/emoji-picker-knobs.d.ts +4 -0
- package/dist/knob-contracts/emoji-picker-knobs.js +10 -0
- package/dist/knob-contracts/field-knobs.d.ts +1 -1
- package/dist/knob-contracts/field-knobs.js +5 -1
- package/dist/knob-contracts/filter-bar-knobs.d.ts +1 -1
- package/dist/knob-contracts/filter-bar-knobs.js +1 -2
- package/dist/knob-contracts/markdown-knobs.d.ts +1 -1
- package/dist/knob-contracts/markdown-knobs.js +3 -1
- package/dist/knob-contracts/page-layout-knobs.d.ts +1 -1
- package/dist/knob-contracts/page-layout-knobs.js +2 -1
- package/dist/knob-contracts/popup-knobs.d.ts +1 -1
- package/dist/knob-contracts/popup-knobs.js +27 -0
- package/dist/knob-contracts/range-knobs.d.ts +1 -1
- package/dist/knob-contracts/range-knobs.js +1 -0
- package/dist/knob-contracts/rich-tooltip-knobs.d.ts +1 -1
- package/dist/knob-contracts/rich-tooltip-knobs.js +2 -1
- package/dist/knob-contracts/sidebar-knobs.d.ts +1 -1
- package/dist/knob-contracts/sidebar-knobs.js +27 -2
- package/dist/knob-contracts/spinner-knobs.d.ts +1 -1
- package/dist/knob-contracts/spinner-knobs.js +1 -1
- package/dist/knob-contracts/stepper-knobs.d.ts +1 -1
- package/dist/knob-contracts/stepper-knobs.js +4 -1
- package/dist/knob-contracts/switch-knobs.d.ts +1 -1
- package/dist/knob-contracts/switch-knobs.js +5 -0
- package/dist/knob-contracts/table-of-contents-knobs.d.ts +1 -1
- package/dist/knob-contracts/table-of-contents-knobs.js +2 -1
- package/dist/knob-contracts/tabs-knobs.d.ts +1 -1
- package/dist/knob-contracts/tabs-knobs.js +1 -0
- package/dist/knob-contracts/tree-knobs.d.ts +1 -1
- package/dist/knob-contracts/tree-knobs.js +2 -1
- package/dist/lib/cn.js +0 -4
- package/dist/lib/contrast.d.ts +1 -0
- package/dist/lib/contrast.js +17 -5
- package/dist/lib/dropzone-validation.js +41 -28
- package/dist/lib/env-file.d.ts +1 -1
- package/dist/lib/env-file.js +5 -5
- package/dist/lib/motion.d.ts +4 -0
- package/dist/lib/motion.js +15 -0
- package/dist/lib/trigger-detect.js +6 -8
- package/dist/markdown-block.js +2 -1
- package/dist/popup-parts.d.ts +8 -0
- package/dist/popup-parts.js +43 -0
- package/dist/source-badge.js +3 -1
- package/dist/styles/code.css +0 -2
- package/dist/styles/effects.css +6 -32
- package/dist/styles/index.css +10 -5
- package/dist/styles/recipes/accordion.css +9 -6
- package/dist/styles/recipes/action-bar.css +0 -15
- package/dist/styles/recipes/activity.css +5 -11
- package/dist/styles/recipes/alert.css +4 -4
- package/dist/styles/recipes/audio-recorder.css +15 -3
- package/dist/styles/recipes/audio-visualizer.css +13 -2
- package/dist/styles/recipes/avatar.css +78 -1
- package/dist/styles/recipes/badge.css +14 -4
- package/dist/styles/recipes/breadcrumb.css +77 -0
- package/dist/styles/recipes/button-group.css +5 -7
- package/dist/styles/recipes/button-states.css +66 -0
- package/dist/styles/recipes/button.css +24 -83
- package/dist/styles/recipes/calendar.css +17 -23
- package/dist/styles/recipes/card.css +2 -2
- package/dist/styles/recipes/chat-composer-attachment.css +9 -3
- package/dist/styles/recipes/chat-composer.css +7 -10
- package/dist/styles/recipes/chat-layout.css +35 -0
- package/dist/styles/recipes/chat-message-social.css +123 -0
- package/dist/styles/recipes/chat-message.css +157 -37
- package/dist/styles/recipes/choice.css +18 -21
- package/dist/styles/recipes/code-diff.css +46 -33
- package/dist/styles/recipes/code.css +36 -16
- package/dist/styles/recipes/collapsible.css +7 -2
- package/dist/styles/recipes/color-picker-details.css +3 -4
- package/dist/styles/recipes/color-picker-values.css +9 -8
- package/dist/styles/recipes/color-picker.css +11 -2
- package/dist/styles/recipes/context.css +3 -3
- package/dist/styles/recipes/dockable-panel.css +7 -4
- package/dist/styles/recipes/dropzone.css +7 -8
- package/dist/styles/recipes/dynamic-notification-input.css +8 -5
- package/dist/styles/recipes/dynamic-notification-motion.css +12 -21
- package/dist/styles/recipes/dynamic-notification.css +40 -26
- package/dist/styles/recipes/emoji-picker.css +132 -0
- package/dist/styles/recipes/empty.css +5 -4
- package/dist/styles/recipes/environment-variables.css +4 -15
- package/dist/styles/recipes/field-parts.css +30 -24
- package/dist/styles/recipes/field.css +61 -26
- package/dist/styles/recipes/filter-bar.css +18 -31
- package/dist/styles/recipes/gradient-editor.css +18 -7
- package/dist/styles/recipes/infinite-canvas.css +0 -1
- package/dist/styles/recipes/inline-attachment.css +3 -3
- package/dist/styles/recipes/inline-citation.css +10 -4
- package/dist/styles/recipes/item.css +23 -9
- package/dist/styles/recipes/markdown-block.css +5 -4
- package/dist/styles/recipes/markdown.css +68 -26
- package/dist/styles/recipes/morphing-panel.css +8 -8
- package/dist/styles/recipes/page-layout.css +20 -12
- package/dist/styles/recipes/pagination.css +4 -5
- package/dist/styles/recipes/phone-input.css +12 -8
- package/dist/styles/recipes/popup-controls.css +166 -33
- package/dist/styles/recipes/popup-drawer.css +3 -14
- package/dist/styles/recipes/popup-lists.css +128 -27
- package/dist/styles/recipes/popup-motion.css +4 -45
- package/dist/styles/recipes/popup-toast.css +55 -26
- package/dist/styles/recipes/popup.css +139 -141
- package/dist/styles/recipes/range.css +54 -18
- package/dist/styles/recipes/resizable.css +2 -7
- package/dist/styles/recipes/rich-tooltip.css +24 -34
- package/dist/styles/recipes/scroll-area.css +8 -1
- package/dist/styles/recipes/sidebar-menu.css +166 -34
- package/dist/styles/recipes/sidebar-parts.css +192 -0
- package/dist/styles/recipes/sidebar.css +188 -128
- package/dist/styles/recipes/source-badge.css +1 -1
- package/dist/styles/recipes/spinner.css +8 -1
- package/dist/styles/recipes/stepper.css +31 -13
- package/dist/styles/recipes/switch.css +68 -23
- package/dist/styles/recipes/table-of-contents.css +9 -1
- package/dist/styles/recipes/table.css +14 -10
- package/dist/styles/recipes/tabs.css +17 -12
- package/dist/styles/recipes/task-list.css +7 -14
- package/dist/styles/recipes/thread-rail.css +42 -19
- package/dist/styles/recipes/timeline.css +2 -0
- package/dist/styles/recipes/toolbar.css +27 -25
- package/dist/styles/recipes/track-highlight.css +10 -7
- package/dist/styles/recipes/tree.css +31 -4
- package/dist/styles/recipes/user-ask-details.css +0 -1
- package/dist/styles/recipes/user-ask.css +4 -4
- package/dist/styles/theme.css +37 -16
- package/dist/task-list.d.ts +4 -1
- package/dist/task-list.js +24 -8
- package/dist/thread-rail.d.ts +1 -1
- package/dist/thread-rail.js +61 -19
- package/dist/transcript-divider.js +2 -1
- package/dist/ui/alert.d.ts +3 -1
- package/dist/ui/alert.js +14 -8
- package/dist/ui/autocomplete.js +3 -1
- package/dist/ui/avatar.d.ts +8 -0
- package/dist/ui/avatar.js +23 -3
- package/dist/ui/badge.js +1 -1
- package/dist/ui/breadcrumb.d.ts +27 -0
- package/dist/ui/breadcrumb.js +88 -0
- package/dist/ui/button.d.ts +2 -1
- package/dist/ui/button.js +1 -1
- package/dist/ui/calendar.js +2 -2
- package/dist/ui/card.d.ts +3 -1
- package/dist/ui/card.js +12 -7
- package/dist/ui/checkbox.d.ts +2 -0
- package/dist/ui/code-diff.d.ts +1 -1
- package/dist/ui/code-diff.js +71 -27
- package/dist/ui/code.d.ts +5 -3
- package/dist/ui/code.js +26 -27
- package/dist/ui/color-picker.d.ts +5 -2
- package/dist/ui/color-picker.js +84 -36
- package/dist/ui/combobox-disabled-selection.d.ts +1 -2
- package/dist/ui/combobox-disabled-selection.js +7 -9
- package/dist/ui/combobox.js +13 -5
- package/dist/ui/command.d.ts +8 -2
- package/dist/ui/command.js +30 -14
- package/dist/ui/context-menu.js +3 -0
- package/dist/ui/dialog.d.ts +2 -1
- package/dist/ui/dialog.js +3 -22
- package/dist/ui/dockable-panel-geometry.d.ts +1 -0
- package/dist/ui/dockable-panel-geometry.js +7 -1
- package/dist/ui/dockable-panel.d.ts +9 -6
- package/dist/ui/dockable-panel.js +63 -23
- package/dist/ui/dropdown-menu.js +4 -3
- package/dist/ui/dropzone.d.ts +2 -2
- package/dist/ui/dropzone.js +75 -16
- package/dist/ui/emoji-picker.d.ts +18 -0
- package/dist/ui/emoji-picker.js +136 -0
- package/dist/ui/empty.d.ts +3 -1
- package/dist/ui/empty.js +12 -7
- package/dist/ui/gradient-editor.d.ts +1 -1
- package/dist/ui/gradient-editor.js +37 -21
- package/dist/ui/infinite-canvas.d.ts +3 -2
- package/dist/ui/infinite-canvas.js +63 -19
- package/dist/ui/input-group.d.ts +0 -3
- package/dist/ui/input-group.js +3 -13
- package/dist/ui/input-otp.d.ts +1 -1
- package/dist/ui/input-otp.js +32 -15
- package/dist/ui/input.js +4 -4
- package/dist/ui/item.d.ts +2 -1
- package/dist/ui/item.js +11 -7
- package/dist/ui/live-status.d.ts +7 -0
- package/dist/ui/live-status.js +17 -0
- package/dist/ui/markdown-elements.d.ts +6 -0
- package/dist/ui/markdown-elements.js +31 -1
- package/dist/ui/menubar.js +2 -2
- package/dist/ui/morphing-panel.js +2 -1
- package/dist/ui/native-select.d.ts +1 -1
- package/dist/ui/native-select.js +24 -11
- package/dist/ui/page-layout.d.ts +2 -2
- package/dist/ui/page-layout.js +2 -4
- package/dist/ui/pagination.d.ts +7 -5
- package/dist/ui/pagination.js +19 -13
- package/dist/ui/phone-input.d.ts +1 -0
- package/dist/ui/phone-input.js +56 -58
- package/dist/ui/radio-group.d.ts +2 -0
- package/dist/ui/resizable.d.ts +3 -2
- package/dist/ui/resizable.js +5 -3
- package/dist/ui/responsive-dialog.d.ts +1 -1
- package/dist/ui/responsive-dialog.js +4 -21
- package/dist/ui/rich-tooltip.d.ts +10 -4
- package/dist/ui/rich-tooltip.js +81 -33
- package/dist/ui/scroll-area.d.ts +1 -1
- package/dist/ui/scroll-area.js +43 -28
- package/dist/ui/sidebar-menu.d.ts +5 -1
- package/dist/ui/sidebar-menu.js +34 -34
- package/dist/ui/sidebar-provider.d.ts +10 -1
- package/dist/ui/sidebar-provider.js +38 -13
- package/dist/ui/sidebar-resize-rail.js +7 -3
- package/dist/ui/sidebar.d.ts +11 -6
- package/dist/ui/sidebar.js +48 -24
- package/dist/ui/slider.js +31 -22
- package/dist/ui/spinner.d.ts +3 -2
- package/dist/ui/spinner.js +3 -2
- package/dist/ui/stepper.d.ts +8 -1
- package/dist/ui/stepper.js +74 -25
- package/dist/ui/switch.d.ts +1 -0
- package/dist/ui/table-of-contents.d.ts +2 -2
- package/dist/ui/table-of-contents.js +20 -8
- package/dist/ui/table.js +4 -2
- package/dist/ui/textarea.d.ts +1 -1
- package/dist/ui/textarea.js +20 -9
- package/dist/ui/timeline.d.ts +3 -1
- package/dist/ui/timeline.js +17 -9
- package/dist/ui/toast.d.ts +11 -5
- package/dist/ui/toast.js +100 -54
- package/dist/ui/toolbar.d.ts +2 -1
- package/dist/ui/toolbar.js +3 -1
- package/dist/ui/tooltip.js +3 -20
- package/dist/ui/tree.js +120 -80
- package/dist/ui/trigger-menu.js +1 -0
- package/dist/user-ask.d.ts +10 -5
- package/dist/user-ask.js +31 -11
- package/package.json +5 -1
- package/dist/hooks/use-keyboard-navigation.d.ts +0 -3
- package/dist/hooks/use-keyboard-navigation.js +0 -22
package/dist/context.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Button } from "./ui/button.js";
|
|
|
4
4
|
import { Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger } from "./ui/popover.js";
|
|
5
5
|
import { ScrollArea } from "./ui/scroll-area.js";
|
|
6
6
|
import { deriveContextModel } from "./context-model.js";
|
|
7
|
-
import { createContext, useContext } from "react";
|
|
7
|
+
import { createContext, useContext, useId, useMemo } from "react";
|
|
8
8
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { X } from "lucide-react";
|
|
10
10
|
//#region src/context.tsx
|
|
@@ -16,14 +16,20 @@ function useContextValue() {
|
|
|
16
16
|
}
|
|
17
17
|
function Context({ segments, maxTokens, model: modelName, locale, open, defaultOpen, onOpenChange, className, children, ...props }) {
|
|
18
18
|
const model = deriveContextModel(segments, maxTokens);
|
|
19
|
+
const numberFormatter = useMemo(() => new Intl.NumberFormat(locale), [locale]);
|
|
20
|
+
const percentageFormatter = useMemo(() => new Intl.NumberFormat(locale, {
|
|
21
|
+
style: "percent",
|
|
22
|
+
maximumFractionDigits: 1
|
|
23
|
+
}), [locale]);
|
|
24
|
+
const percentage = model.ratio === null ? null : percentageFormatter.format(model.ratio);
|
|
25
|
+
const usedTokens = numberFormatter.format(model.usedTokens);
|
|
19
26
|
const context = {
|
|
20
27
|
model,
|
|
21
28
|
modelName,
|
|
22
|
-
numberFormatter
|
|
23
|
-
percentageFormatter
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
})
|
|
29
|
+
numberFormatter,
|
|
30
|
+
percentageFormatter,
|
|
31
|
+
percentage,
|
|
32
|
+
usage: model.maxTokens === null ? `${usedTokens} tokens used; limit unavailable` : `${usedTokens} of ${numberFormatter.format(model.maxTokens)} tokens used (${percentage})${model.overageTokens > 0 ? `; ${numberFormatter.format(model.overageTokens)} tokens over limit` : ""}`
|
|
27
33
|
};
|
|
28
34
|
return /* @__PURE__ */ jsx(ContextValueContext.Provider, {
|
|
29
35
|
value: context,
|
|
@@ -43,24 +49,22 @@ function Context({ segments, maxTokens, model: modelName, locale, open, defaultO
|
|
|
43
49
|
})
|
|
44
50
|
});
|
|
45
51
|
}
|
|
46
|
-
function ContextTrigger({ "aria-
|
|
47
|
-
const { model,
|
|
48
|
-
const
|
|
52
|
+
function ContextTrigger({ "aria-describedby": describedBy, variant = "surface", size = "sm", className, children, ...props }) {
|
|
53
|
+
const { model, percentage, usage } = useContextValue();
|
|
54
|
+
const usageId = useId();
|
|
49
55
|
const visualPercentage = Math.min(100, Math.max(0, (model.ratio ?? 0) * 100));
|
|
50
|
-
|
|
51
|
-
const accessibleLabel = model.maxTokens === null ? `Context window: ${numberFormatter.format(model.usedTokens)} tokens used; limit unavailable` : `Context window: ${numberFormatter.format(model.usedTokens)} of ${numberFormatter.format(model.maxTokens)} tokens used (${percentage})${model.overageTokens > 0 ? `; ${numberFormatter.format(model.overageTokens)} tokens over limit` : ""}`;
|
|
52
|
-
return /* @__PURE__ */ jsx("span", {
|
|
56
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
53
57
|
"data-control-ui": "context",
|
|
54
58
|
"data-control-family": "context",
|
|
55
59
|
"data-slot": "trigger",
|
|
56
60
|
"data-status": model.status,
|
|
57
61
|
className: "relative inline-flex",
|
|
58
|
-
children: /* @__PURE__ */ jsx(Button, {
|
|
62
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
59
63
|
...props,
|
|
60
64
|
render: /* @__PURE__ */ jsx(PopoverTrigger, {}),
|
|
61
65
|
variant,
|
|
62
66
|
size,
|
|
63
|
-
"aria-
|
|
67
|
+
"aria-describedby": describedBy ? `${usageId} ${describedBy}` : usageId,
|
|
64
68
|
className: cn("group/context after:absolute after:-inset-1.5 after:content-['']", className),
|
|
65
69
|
children: children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("svg", {
|
|
66
70
|
"data-control-ui": "context",
|
|
@@ -96,9 +100,13 @@ function ContextTrigger({ "aria-label": ariaLabel, variant = "surface", size = "
|
|
|
96
100
|
"data-control-ui": "context",
|
|
97
101
|
"data-control-family": "context",
|
|
98
102
|
"data-slot": "trigger-label",
|
|
99
|
-
children:
|
|
103
|
+
children: percentage === null ? "Context unavailable" : `${percentage} context`
|
|
100
104
|
})] })
|
|
101
|
-
})
|
|
105
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
106
|
+
id: usageId,
|
|
107
|
+
className: "sr-only",
|
|
108
|
+
children: usage
|
|
109
|
+
})]
|
|
102
110
|
});
|
|
103
111
|
}
|
|
104
112
|
function ContextContent({ "aria-label": ariaLabel, side = "top", align = "end", sideOffset = 8, collisionPadding = 16, padding = "none", className, children, ...props }) {
|
|
@@ -170,8 +178,7 @@ function ContextDescription({ className, children, ...props }) {
|
|
|
170
178
|
});
|
|
171
179
|
}
|
|
172
180
|
function ContextSummary({ className, children, ...props }) {
|
|
173
|
-
const { model, numberFormatter,
|
|
174
|
-
const percentage = model.ratio === null ? null : percentageFormatter.format(model.ratio);
|
|
181
|
+
const { model, numberFormatter, percentage } = useContextValue();
|
|
175
182
|
return /* @__PURE__ */ jsx("div", {
|
|
176
183
|
...props,
|
|
177
184
|
"data-control-ui": "context",
|
|
@@ -183,21 +190,29 @@ function ContextSummary({ className, children, ...props }) {
|
|
|
183
190
|
"data-control-ui": "context",
|
|
184
191
|
"data-control-family": "context",
|
|
185
192
|
"data-slot": "summary-value",
|
|
186
|
-
children: model.maxTokens === null ? "Limit unavailable" : `${numberFormatter.format(model.usedTokens)}
|
|
193
|
+
children: model.maxTokens === null ? "Limit unavailable" : `${numberFormatter.format(model.usedTokens)} of ${numberFormatter.format(model.maxTokens)} tokens`
|
|
187
194
|
})] })
|
|
188
195
|
});
|
|
189
196
|
}
|
|
190
|
-
function ContextGraph({ className, ...props }) {
|
|
191
|
-
const { model } = useContextValue();
|
|
197
|
+
function ContextGraph({ "aria-label": ariaLabel = "Context usage", className, ...props }) {
|
|
198
|
+
const { model, usage } = useContextValue();
|
|
199
|
+
const meterProps = model.maxTokens === null ? { "aria-hidden": true } : {
|
|
200
|
+
role: "meter",
|
|
201
|
+
"aria-label": ariaLabel,
|
|
202
|
+
"aria-valuemin": 0,
|
|
203
|
+
"aria-valuemax": model.maxTokens,
|
|
204
|
+
"aria-valuenow": Math.min(model.usedTokens, model.maxTokens),
|
|
205
|
+
"aria-valuetext": usage
|
|
206
|
+
};
|
|
192
207
|
return /* @__PURE__ */ jsxs("svg", {
|
|
193
208
|
...props,
|
|
209
|
+
...meterProps,
|
|
194
210
|
"data-control-ui": "context",
|
|
195
211
|
"data-control-family": "context",
|
|
196
212
|
"data-slot": "graph",
|
|
197
213
|
"data-status": model.status,
|
|
198
214
|
viewBox: "0 0 100 10",
|
|
199
215
|
preserveAspectRatio: "none",
|
|
200
|
-
"aria-hidden": "true",
|
|
201
216
|
className: cn("w-full overflow-hidden", className),
|
|
202
217
|
children: [
|
|
203
218
|
/* @__PURE__ */ jsx("rect", {
|
|
@@ -322,7 +337,7 @@ function ContextClose({ variant = "ghost", size = "xs", iconOnly = true, "aria-l
|
|
|
322
337
|
size,
|
|
323
338
|
iconOnly,
|
|
324
339
|
"aria-label": ariaLabel,
|
|
325
|
-
className: cn("
|
|
340
|
+
className: cn("ms-auto", className),
|
|
326
341
|
children: children ?? /* @__PURE__ */ jsx(X, {
|
|
327
342
|
"aria-hidden": "true",
|
|
328
343
|
className: "size-4"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/dynamic-notification-canvas-loop.d.ts
|
|
2
|
+
export type NotificationCanvasFrame = {
|
|
3
|
+
reduced: boolean;
|
|
4
|
+
now: number;
|
|
5
|
+
deltaMs: number;
|
|
6
|
+
};
|
|
7
|
+
export type NotificationCanvasLoop = {
|
|
8
|
+
invalidate: () => void;
|
|
9
|
+
cancel: () => void;
|
|
10
|
+
destroy: () => void;
|
|
11
|
+
};
|
|
12
|
+
export type NotificationMotionValues = {
|
|
13
|
+
radius: number;
|
|
14
|
+
aurora: number;
|
|
15
|
+
reveal: number;
|
|
16
|
+
settle: number;
|
|
17
|
+
lift: number;
|
|
18
|
+
};
|
|
19
|
+
export declare function createNotificationMotion(initialState: string | undefined): {
|
|
20
|
+
values: NotificationMotionValues;
|
|
21
|
+
advance: (state: string | undefined, radius: number, reduced: boolean, deltaMs: number) => boolean;
|
|
22
|
+
};
|
|
23
|
+
export declare function createNotificationCanvasLoop(canvas: HTMLCanvasElement, render: (frame: NotificationCanvasFrame) => boolean): NotificationCanvasLoop;
|
|
24
|
+
//#endregion
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { prefersReducedMotion } from "./lib/motion.js";
|
|
2
|
+
//#region src/dynamic-notification-canvas-loop.ts
|
|
3
|
+
const FRAME_MS_AT_60HZ = 1e3 / 60;
|
|
4
|
+
const MAX_FRAME_DELTA_MS = 100;
|
|
5
|
+
function approachAt60Hz(current, target, response, deltaMs) {
|
|
6
|
+
const frameScale = Math.max(0, deltaMs) / FRAME_MS_AT_60HZ;
|
|
7
|
+
return current + (target - current) * (1 - (1 - response) ** frameScale);
|
|
8
|
+
}
|
|
9
|
+
function motionTargets(state, radius) {
|
|
10
|
+
const thinking = state === "thinking";
|
|
11
|
+
return {
|
|
12
|
+
radius,
|
|
13
|
+
aurora: thinking ? 1 : 0,
|
|
14
|
+
reveal: state === "collapsed" ? 0 : 1,
|
|
15
|
+
settle: state === "expanded" ? 1 : 0,
|
|
16
|
+
lift: thinking ? 0 : 1
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function createNotificationMotion(initialState) {
|
|
20
|
+
const values = motionTargets(initialState, 0);
|
|
21
|
+
function advance(state, radius, reduced, deltaMs) {
|
|
22
|
+
const targets = motionTargets(state, radius);
|
|
23
|
+
if (reduced) {
|
|
24
|
+
Object.assign(values, targets);
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
values.radius = approachAt60Hz(values.radius, targets.radius, .25, deltaMs);
|
|
28
|
+
values.aurora = approachAt60Hz(values.aurora, targets.aurora, .06, deltaMs);
|
|
29
|
+
values.reveal = approachAt60Hz(values.reveal, targets.reveal, .07, deltaMs);
|
|
30
|
+
values.settle = approachAt60Hz(values.settle, targets.settle, .07, deltaMs);
|
|
31
|
+
values.lift = targets.lift === 1 ? Math.min(1, values.lift + deltaMs * .00216) : approachAt60Hz(values.lift, 0, .28, deltaMs);
|
|
32
|
+
return state === "thinking" || Math.abs(values.radius - targets.radius) > .05 || Math.abs(values.aurora - targets.aurora) > .002 || Math.abs(values.reveal - targets.reveal) > .002 || Math.abs(values.settle - targets.settle) > .002 || Math.abs(values.lift - targets.lift) > .002;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
values,
|
|
36
|
+
advance
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function createNotificationCanvasLoop(canvas, render) {
|
|
40
|
+
let destroyed = false;
|
|
41
|
+
let intersecting = true;
|
|
42
|
+
let pageVisible = !document.hidden;
|
|
43
|
+
let rafId = 0;
|
|
44
|
+
let staticFrameDrawn = false;
|
|
45
|
+
let previousFrameAt = performance.now();
|
|
46
|
+
const reducedMotionQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
47
|
+
function tick() {
|
|
48
|
+
rafId = 0;
|
|
49
|
+
if (destroyed || !intersecting || !pageVisible) return;
|
|
50
|
+
const now = performance.now();
|
|
51
|
+
const deltaMs = Math.min(MAX_FRAME_DELTA_MS, now - previousFrameAt);
|
|
52
|
+
previousFrameAt = now;
|
|
53
|
+
const reduced = prefersReducedMotion(canvas);
|
|
54
|
+
const keepAnimating = render({
|
|
55
|
+
reduced,
|
|
56
|
+
now,
|
|
57
|
+
deltaMs
|
|
58
|
+
}) && !reduced;
|
|
59
|
+
staticFrameDrawn = !keepAnimating;
|
|
60
|
+
if (keepAnimating) rafId = requestAnimationFrame(tick);
|
|
61
|
+
}
|
|
62
|
+
function invalidate() {
|
|
63
|
+
staticFrameDrawn = false;
|
|
64
|
+
if (destroyed || !intersecting || !pageVisible || rafId !== 0) return;
|
|
65
|
+
previousFrameAt = performance.now();
|
|
66
|
+
rafId = requestAnimationFrame(tick);
|
|
67
|
+
}
|
|
68
|
+
function cancel() {
|
|
69
|
+
if (rafId !== 0) cancelAnimationFrame(rafId);
|
|
70
|
+
rafId = 0;
|
|
71
|
+
}
|
|
72
|
+
function handleVisibility() {
|
|
73
|
+
pageVisible = !document.hidden;
|
|
74
|
+
invalidate();
|
|
75
|
+
}
|
|
76
|
+
const resizeObserver = new ResizeObserver(invalidate);
|
|
77
|
+
resizeObserver.observe(canvas);
|
|
78
|
+
const intersectionObserver = new IntersectionObserver((entries) => {
|
|
79
|
+
for (const entry of entries) intersecting = entry.isIntersecting;
|
|
80
|
+
invalidate();
|
|
81
|
+
}, { rootMargin: "64px" });
|
|
82
|
+
intersectionObserver.observe(canvas);
|
|
83
|
+
const motionObserver = new MutationObserver(() => {
|
|
84
|
+
if (!staticFrameDrawn || !prefersReducedMotion(canvas)) invalidate();
|
|
85
|
+
});
|
|
86
|
+
motionObserver.observe(document.documentElement, {
|
|
87
|
+
attributes: true,
|
|
88
|
+
attributeFilter: ["data-motion"]
|
|
89
|
+
});
|
|
90
|
+
const stateObserver = new MutationObserver(invalidate);
|
|
91
|
+
if (canvas.parentElement) stateObserver.observe(canvas.parentElement, {
|
|
92
|
+
attributes: true,
|
|
93
|
+
attributeFilter: ["data-state"]
|
|
94
|
+
});
|
|
95
|
+
document.addEventListener("visibilitychange", handleVisibility);
|
|
96
|
+
reducedMotionQuery.addEventListener("change", invalidate);
|
|
97
|
+
return {
|
|
98
|
+
invalidate,
|
|
99
|
+
cancel,
|
|
100
|
+
destroy() {
|
|
101
|
+
destroyed = true;
|
|
102
|
+
cancel();
|
|
103
|
+
resizeObserver.disconnect();
|
|
104
|
+
intersectionObserver.disconnect();
|
|
105
|
+
motionObserver.disconnect();
|
|
106
|
+
stateObserver.disconnect();
|
|
107
|
+
document.removeEventListener("visibilitychange", handleVisibility);
|
|
108
|
+
reducedMotionQuery.removeEventListener("change", invalidate);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
export { createNotificationCanvasLoop, createNotificationMotion };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createNotificationCanvasLoop, createNotificationMotion } from "./dynamic-notification-canvas-loop.js";
|
|
1
2
|
import { DYNAMIC_NOTIFICATION_SIRI_WAVE_GLSL } from "./dynamic-notification-siri-wave.js";
|
|
2
3
|
//#region src/dynamic-notification-glass.ts
|
|
3
4
|
const VERTEX_SHADER = `
|
|
@@ -141,15 +142,6 @@ function buildProgram(gl) {
|
|
|
141
142
|
ribbonHorizon: gl.getUniformLocation(program, "u_ribbonHorizon")
|
|
142
143
|
};
|
|
143
144
|
}
|
|
144
|
-
function auroraTarget(state) {
|
|
145
|
-
return state === "thinking" ? 1 : 0;
|
|
146
|
-
}
|
|
147
|
-
function revealTarget(state) {
|
|
148
|
-
return state === "collapsed" ? 0 : 1;
|
|
149
|
-
}
|
|
150
|
-
function settleTarget(state) {
|
|
151
|
-
return state === "expanded" ? 1 : 0;
|
|
152
|
-
}
|
|
153
145
|
function createDynamicNotificationGlass(canvas, options = {}) {
|
|
154
146
|
const { intensity = 1, maxDpr = 2 } = options;
|
|
155
147
|
const gl = canvas.getContext("webgl", {
|
|
@@ -161,28 +153,9 @@ function createDynamicNotificationGlass(canvas, options = {}) {
|
|
|
161
153
|
let contextLost = gl.isContextLost();
|
|
162
154
|
let glass = contextLost ? null : buildProgram(gl);
|
|
163
155
|
if (!contextLost && !glass) return () => {};
|
|
164
|
-
let destroyed = false;
|
|
165
|
-
let intersecting = true;
|
|
166
|
-
let pageVisible = !document.hidden;
|
|
167
|
-
let rafId = 0;
|
|
168
156
|
let dpr = 1;
|
|
169
|
-
|
|
170
|
-
let aurora = auroraTarget(canvas.parentElement?.dataset.state);
|
|
171
|
-
let lift = 1 - aurora;
|
|
172
|
-
let reveal = revealTarget(canvas.parentElement?.dataset.state);
|
|
173
|
-
let settle = settleTarget(canvas.parentElement?.dataset.state);
|
|
174
|
-
let staticFrameDrawn = false;
|
|
157
|
+
const motion = createNotificationMotion(canvas.parentElement?.dataset.state);
|
|
175
158
|
const startedAt = performance.now();
|
|
176
|
-
const reducedMotionQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
177
|
-
function reducedMotion() {
|
|
178
|
-
return reducedMotionQuery.matches || canvas.closest("[data-motion=\"reduced\"]") !== null;
|
|
179
|
-
}
|
|
180
|
-
function targetRadius() {
|
|
181
|
-
const host = canvas.parentElement;
|
|
182
|
-
if (!host) return 0;
|
|
183
|
-
const parsed = Number.parseFloat(getComputedStyle(host).borderTopLeftRadius);
|
|
184
|
-
return Number.isFinite(parsed) ? parsed * dpr : 0;
|
|
185
|
-
}
|
|
186
159
|
function resize() {
|
|
187
160
|
dpr = Math.min(window.devicePixelRatio || 1, maxDpr);
|
|
188
161
|
const width = Math.max(1, Math.round(canvas.clientWidth * dpr));
|
|
@@ -192,111 +165,47 @@ function createDynamicNotificationGlass(canvas, options = {}) {
|
|
|
192
165
|
canvas.height = height;
|
|
193
166
|
}
|
|
194
167
|
}
|
|
195
|
-
function
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
const auroraGoal = auroraTarget(canvas.parentElement?.dataset.state);
|
|
199
|
-
aurora = reduced ? auroraGoal : aurora + (auroraGoal - aurora) * .06;
|
|
200
|
-
const liftGoal = auroraGoal >= .5 ? 0 : 1;
|
|
201
|
-
if (reduced) lift = liftGoal;
|
|
202
|
-
else if (liftGoal === 1) lift = Math.min(1, lift + .036);
|
|
203
|
-
else lift *= .72;
|
|
204
|
-
const revealGoal = revealTarget(canvas.parentElement?.dataset.state);
|
|
205
|
-
reveal = reduced ? revealGoal : reveal + (revealGoal - reveal) * .07;
|
|
206
|
-
const settleGoal = settleTarget(canvas.parentElement?.dataset.state);
|
|
207
|
-
settle = reduced ? settleGoal : settle + (settleGoal - settle) * .07;
|
|
208
|
-
}
|
|
209
|
-
function uploadUniforms(activeGl, activeGlass, reduced) {
|
|
210
|
-
activeGl.uniform2f(activeGlass.resolution, canvas.width, canvas.height);
|
|
211
|
-
activeGl.uniform1f(activeGlass.time, reduced ? 4.2 : (performance.now() - startedAt) / 1e3);
|
|
212
|
-
activeGl.uniform1f(activeGlass.radius, radius);
|
|
213
|
-
activeGl.uniform1f(activeGlass.intensity, intensity);
|
|
214
|
-
activeGl.uniform1f(activeGlass.aurora, aurora);
|
|
215
|
-
activeGl.uniform1f(activeGlass.reveal, reveal);
|
|
216
|
-
activeGl.uniform1f(activeGlass.settle, settle);
|
|
217
|
-
activeGl.uniform1f(activeGlass.lift, lift);
|
|
218
|
-
activeGl.uniform1f(activeGlass.ribbonHorizon, .5);
|
|
219
|
-
}
|
|
220
|
-
function draw() {
|
|
221
|
-
if (!gl || !glass || contextLost) return;
|
|
168
|
+
function render({ reduced, now, deltaMs }) {
|
|
169
|
+
const host = canvas.parentElement;
|
|
170
|
+
if (!gl || !glass || contextLost || !host) return false;
|
|
222
171
|
resize();
|
|
172
|
+
const hostRadius = Number.parseFloat(getComputedStyle(host).borderTopLeftRadius);
|
|
173
|
+
const keepAnimating = motion.advance(host.dataset.state, Number.isFinite(hostRadius) ? hostRadius * dpr : 0, reduced, deltaMs);
|
|
174
|
+
const { radius, aurora, reveal, settle, lift } = motion.values;
|
|
223
175
|
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
224
176
|
gl.clearColor(0, 0, 0, 0);
|
|
225
177
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
226
178
|
gl.useProgram(glass.program);
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
179
|
+
gl.uniform2f(glass.resolution, canvas.width, canvas.height);
|
|
180
|
+
gl.uniform1f(glass.time, reduced ? 4.2 : (now - startedAt) / 1e3);
|
|
181
|
+
gl.uniform1f(glass.radius, radius);
|
|
182
|
+
gl.uniform1f(glass.intensity, intensity);
|
|
183
|
+
gl.uniform1f(glass.aurora, aurora);
|
|
184
|
+
gl.uniform1f(glass.reveal, reveal);
|
|
185
|
+
gl.uniform1f(glass.settle, settle);
|
|
186
|
+
gl.uniform1f(glass.lift, lift);
|
|
187
|
+
gl.uniform1f(glass.ribbonHorizon, .5);
|
|
230
188
|
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
231
189
|
if (canvas.dataset.glassReady !== "true") canvas.dataset.glassReady = "true";
|
|
190
|
+
return keepAnimating;
|
|
232
191
|
}
|
|
233
|
-
|
|
234
|
-
return intersecting && pageVisible;
|
|
235
|
-
}
|
|
236
|
-
function tick() {
|
|
237
|
-
rafId = 0;
|
|
238
|
-
if (destroyed || !visible() || contextLost) return;
|
|
239
|
-
draw();
|
|
240
|
-
if (reducedMotion()) {
|
|
241
|
-
staticFrameDrawn = true;
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
staticFrameDrawn = false;
|
|
245
|
-
rafId = requestAnimationFrame(tick);
|
|
246
|
-
}
|
|
247
|
-
function invalidate() {
|
|
248
|
-
staticFrameDrawn = false;
|
|
249
|
-
if (destroyed || !visible() || contextLost || rafId !== 0) return;
|
|
250
|
-
rafId = requestAnimationFrame(tick);
|
|
251
|
-
}
|
|
252
|
-
function handleVisibility() {
|
|
253
|
-
pageVisible = !document.hidden;
|
|
254
|
-
invalidate();
|
|
255
|
-
}
|
|
192
|
+
const loop = createNotificationCanvasLoop(canvas, render);
|
|
256
193
|
function handleContextLost(event) {
|
|
257
194
|
event.preventDefault();
|
|
258
195
|
contextLost = true;
|
|
259
|
-
|
|
260
|
-
rafId = 0;
|
|
196
|
+
loop.cancel();
|
|
261
197
|
}
|
|
262
198
|
function handleContextRestored() {
|
|
263
199
|
if (!gl) return;
|
|
264
200
|
contextLost = false;
|
|
265
201
|
glass = buildProgram(gl);
|
|
266
|
-
invalidate();
|
|
267
|
-
}
|
|
268
|
-
const resizeObserver = new ResizeObserver(() => {
|
|
269
|
-
invalidate();
|
|
270
|
-
});
|
|
271
|
-
resizeObserver.observe(canvas);
|
|
272
|
-
const intersectionObserver = new IntersectionObserver((entries) => {
|
|
273
|
-
for (const entry of entries) intersecting = entry.isIntersecting;
|
|
274
|
-
invalidate();
|
|
275
|
-
}, { rootMargin: "64px" });
|
|
276
|
-
intersectionObserver.observe(canvas);
|
|
277
|
-
const motionObserver = new MutationObserver(() => {
|
|
278
|
-
if (!staticFrameDrawn || !reducedMotion()) invalidate();
|
|
279
|
-
});
|
|
280
|
-
motionObserver.observe(document.documentElement, {
|
|
281
|
-
attributes: true,
|
|
282
|
-
attributeFilter: ["data-motion"]
|
|
283
|
-
});
|
|
284
|
-
function handleMotionPreference() {
|
|
285
|
-
invalidate();
|
|
202
|
+
loop.invalidate();
|
|
286
203
|
}
|
|
287
|
-
document.addEventListener("visibilitychange", handleVisibility);
|
|
288
|
-
reducedMotionQuery.addEventListener("change", handleMotionPreference);
|
|
289
204
|
canvas.addEventListener("webglcontextlost", handleContextLost);
|
|
290
205
|
canvas.addEventListener("webglcontextrestored", handleContextRestored);
|
|
291
|
-
invalidate();
|
|
206
|
+
loop.invalidate();
|
|
292
207
|
return () => {
|
|
293
|
-
|
|
294
|
-
if (rafId !== 0) cancelAnimationFrame(rafId);
|
|
295
|
-
resizeObserver.disconnect();
|
|
296
|
-
intersectionObserver.disconnect();
|
|
297
|
-
motionObserver.disconnect();
|
|
298
|
-
document.removeEventListener("visibilitychange", handleVisibility);
|
|
299
|
-
reducedMotionQuery.removeEventListener("change", handleMotionPreference);
|
|
208
|
+
loop.destroy();
|
|
300
209
|
canvas.removeEventListener("webglcontextlost", handleContextLost);
|
|
301
210
|
canvas.removeEventListener("webglcontextrestored", handleContextRestored);
|
|
302
211
|
delete canvas.dataset.glassReady;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createNotificationCanvasLoop, createNotificationMotion } from "./dynamic-notification-canvas-loop.js";
|
|
1
2
|
import { DYNAMIC_NOTIFICATION_SIRI_WAVE_GLSL } from "./dynamic-notification-siri-wave.js";
|
|
2
3
|
import { LIQUID_GLASS_OPTICS_GLSL } from "./lib/liquid-glass-optics.js";
|
|
3
4
|
//#region src/dynamic-notification-liquid.ts
|
|
@@ -192,19 +193,6 @@ function buildProgram(gl) {
|
|
|
192
193
|
ribbonHorizon: gl.getUniformLocation(program, "u_ribbonHorizon")
|
|
193
194
|
};
|
|
194
195
|
}
|
|
195
|
-
function auroraTarget(state) {
|
|
196
|
-
return state === "thinking" ? 1 : 0;
|
|
197
|
-
}
|
|
198
|
-
function revealTarget(state) {
|
|
199
|
-
return state === "collapsed" ? 0 : 1;
|
|
200
|
-
}
|
|
201
|
-
function settleTarget(state) {
|
|
202
|
-
return state === "expanded" ? 1 : 0;
|
|
203
|
-
}
|
|
204
|
-
function approachAt60Hz(current, target, response, deltaMs) {
|
|
205
|
-
const frameScale = Math.max(0, deltaMs) / (1e3 / 60);
|
|
206
|
-
return current + (target - current) * (1 - (1 - response) ** frameScale);
|
|
207
|
-
}
|
|
208
196
|
const EXCLUDE_SELECTOR = "[data-control-ui=\"dynamic-notification\"]";
|
|
209
197
|
function cssImageUrls(value) {
|
|
210
198
|
return Array.from(value.matchAll(/url\((["']?)(.*?)\1\)/g), (match) => match[2]?.trim()).filter((url) => Boolean(url && !url.startsWith("data:") && !url.startsWith("#")));
|
|
@@ -434,31 +422,13 @@ function createDynamicNotificationLiquid(canvas, options = {}) {
|
|
|
434
422
|
};
|
|
435
423
|
}
|
|
436
424
|
let destroyed = false;
|
|
437
|
-
let intersecting = true;
|
|
438
|
-
let pageVisible = !document.hidden;
|
|
439
|
-
let rafId = 0;
|
|
440
425
|
let dpr = 1;
|
|
441
|
-
|
|
442
|
-
const initialState = canvas.parentElement?.dataset.state;
|
|
443
|
-
let aurora = auroraTarget(initialState);
|
|
444
|
-
let reveal = revealTarget(initialState);
|
|
445
|
-
let settle = settleTarget(initialState);
|
|
446
|
-
let lift = 1 - aurora;
|
|
447
|
-
let staticFrameDrawn = false;
|
|
426
|
+
const motion = createNotificationMotion(canvas.parentElement?.dataset.state);
|
|
448
427
|
let captured = null;
|
|
449
428
|
let textureFresh = false;
|
|
450
429
|
let captureToken = 0;
|
|
451
430
|
let captureTimer = 0;
|
|
452
431
|
const startedAt = performance.now();
|
|
453
|
-
let previousFrameAt = startedAt;
|
|
454
|
-
const reducedMotionQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
455
|
-
function reducedMotion() {
|
|
456
|
-
return reducedMotionQuery.matches || canvas.closest("[data-motion=\"reduced\"]") !== null;
|
|
457
|
-
}
|
|
458
|
-
function targetRadius(host) {
|
|
459
|
-
const parsed = Number.parseFloat(getComputedStyle(host).borderTopLeftRadius);
|
|
460
|
-
return Number.isFinite(parsed) ? parsed : 0;
|
|
461
|
-
}
|
|
462
432
|
function resize() {
|
|
463
433
|
dpr = Math.min(window.devicePixelRatio || 1, maxDpr);
|
|
464
434
|
const width = Math.max(1, Math.round(canvas.clientWidth * dpr));
|
|
@@ -494,36 +464,14 @@ function createDynamicNotificationLiquid(canvas, options = {}) {
|
|
|
494
464
|
captured = result;
|
|
495
465
|
delete canvas.dataset.glassFailed;
|
|
496
466
|
uploadTexture();
|
|
497
|
-
invalidate();
|
|
467
|
+
loop.invalidate();
|
|
498
468
|
});
|
|
499
469
|
}
|
|
500
470
|
function scheduleRecapture() {
|
|
501
471
|
window.clearTimeout(captureTimer);
|
|
502
472
|
captureTimer = window.setTimeout(recapture, 200);
|
|
503
473
|
}
|
|
504
|
-
function
|
|
505
|
-
const state = host.dataset.state;
|
|
506
|
-
const targetAurora = auroraTarget(state);
|
|
507
|
-
const targets = {
|
|
508
|
-
radius: targetRadius(host),
|
|
509
|
-
aurora: targetAurora,
|
|
510
|
-
reveal: revealTarget(state),
|
|
511
|
-
settle: settleTarget(state),
|
|
512
|
-
lift: targetAurora >= .5 ? 0 : 1
|
|
513
|
-
};
|
|
514
|
-
radius = reduced ? targets.radius : approachAt60Hz(radius, targets.radius, .25, deltaMs);
|
|
515
|
-
aurora = reduced ? targets.aurora : approachAt60Hz(aurora, targets.aurora, .06, deltaMs);
|
|
516
|
-
reveal = reduced ? targets.reveal : approachAt60Hz(reveal, targets.reveal, .07, deltaMs);
|
|
517
|
-
settle = reduced ? targets.settle : approachAt60Hz(settle, targets.settle, .07, deltaMs);
|
|
518
|
-
if (reduced) lift = targets.lift;
|
|
519
|
-
else if (targets.lift === 1) lift = Math.min(1, lift + deltaMs * .00216);
|
|
520
|
-
else lift = approachAt60Hz(lift, 0, .28, deltaMs);
|
|
521
|
-
return targets;
|
|
522
|
-
}
|
|
523
|
-
function shouldAnimate(host, reduced, targets) {
|
|
524
|
-
return !reduced && (host.dataset.state === "thinking" || Math.abs(radius - targets.radius) > .05 || Math.abs(aurora - targets.aurora) > .002 || Math.abs(reveal - targets.reveal) > .002 || Math.abs(settle - targets.settle) > .002 || Math.abs(lift - targets.lift) > .002);
|
|
525
|
-
}
|
|
526
|
-
function draw() {
|
|
474
|
+
function render({ reduced, now, deltaMs }) {
|
|
527
475
|
if (!gl || !liquid || contextLost || !textureFresh) return false;
|
|
528
476
|
const host = canvas.parentElement;
|
|
529
477
|
if (!host) return false;
|
|
@@ -531,15 +479,13 @@ function createDynamicNotificationLiquid(canvas, options = {}) {
|
|
|
531
479
|
const hostRect = host.getBoundingClientRect();
|
|
532
480
|
const sceneRect = scene.getBoundingClientRect();
|
|
533
481
|
if (sceneRect.width < 1 || sceneRect.height < 1) return false;
|
|
482
|
+
const hostRadius = Number.parseFloat(getComputedStyle(host).borderTopLeftRadius);
|
|
483
|
+
const keepAnimating = motion.advance(host.dataset.state, Number.isFinite(hostRadius) ? hostRadius : 0, reduced, deltaMs);
|
|
484
|
+
const { radius, aurora, reveal, settle, lift } = motion.values;
|
|
534
485
|
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
535
486
|
gl.clearColor(0, 0, 0, 0);
|
|
536
487
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
537
488
|
gl.useProgram(liquid.program);
|
|
538
|
-
const reduced = reducedMotion();
|
|
539
|
-
const now = performance.now();
|
|
540
|
-
const deltaMs = Math.min(100, now - previousFrameAt);
|
|
541
|
-
previousFrameAt = now;
|
|
542
|
-
const targets = advanceMotion(host, reduced, deltaMs);
|
|
543
489
|
gl.activeTexture(gl.TEXTURE0);
|
|
544
490
|
gl.bindTexture(gl.TEXTURE_2D, liquid.sharpTexture);
|
|
545
491
|
gl.activeTexture(gl.TEXTURE1);
|
|
@@ -566,38 +512,16 @@ function createDynamicNotificationLiquid(canvas, options = {}) {
|
|
|
566
512
|
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
567
513
|
delete canvas.dataset.glassFailed;
|
|
568
514
|
if (canvas.dataset.glassReady !== "true") canvas.dataset.glassReady = "true";
|
|
569
|
-
return
|
|
570
|
-
}
|
|
571
|
-
function visible() {
|
|
572
|
-
return intersecting && pageVisible;
|
|
573
|
-
}
|
|
574
|
-
function tick() {
|
|
575
|
-
rafId = 0;
|
|
576
|
-
if (destroyed || !visible() || contextLost) return;
|
|
577
|
-
if (!draw()) {
|
|
578
|
-
staticFrameDrawn = true;
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
staticFrameDrawn = false;
|
|
582
|
-
rafId = requestAnimationFrame(tick);
|
|
583
|
-
}
|
|
584
|
-
function invalidate() {
|
|
585
|
-
staticFrameDrawn = false;
|
|
586
|
-
if (destroyed || !visible() || contextLost || rafId !== 0) return;
|
|
587
|
-
rafId = requestAnimationFrame(tick);
|
|
588
|
-
}
|
|
589
|
-
function handleVisibility() {
|
|
590
|
-
pageVisible = !document.hidden;
|
|
591
|
-
invalidate();
|
|
515
|
+
return keepAnimating;
|
|
592
516
|
}
|
|
517
|
+
const loop = createNotificationCanvasLoop(canvas, render);
|
|
593
518
|
function handleContextLost(event) {
|
|
594
519
|
event.preventDefault();
|
|
595
520
|
contextLost = true;
|
|
596
521
|
textureFresh = false;
|
|
597
522
|
delete canvas.dataset.glassReady;
|
|
598
523
|
canvas.dataset.glassFailed = "context";
|
|
599
|
-
|
|
600
|
-
rafId = 0;
|
|
524
|
+
loop.cancel();
|
|
601
525
|
}
|
|
602
526
|
function handleContextRestored() {
|
|
603
527
|
if (!gl) return;
|
|
@@ -608,15 +532,9 @@ function createDynamicNotificationLiquid(canvas, options = {}) {
|
|
|
608
532
|
return;
|
|
609
533
|
}
|
|
610
534
|
uploadTexture();
|
|
611
|
-
invalidate();
|
|
535
|
+
loop.invalidate();
|
|
612
536
|
}
|
|
613
|
-
const
|
|
614
|
-
invalidate();
|
|
615
|
-
});
|
|
616
|
-
resizeObserver.observe(canvas);
|
|
617
|
-
const sceneResizeObserver = new ResizeObserver(() => {
|
|
618
|
-
scheduleRecapture();
|
|
619
|
-
});
|
|
537
|
+
const sceneResizeObserver = new ResizeObserver(scheduleRecapture);
|
|
620
538
|
sceneResizeObserver.observe(scene);
|
|
621
539
|
const sceneMutationObserver = new MutationObserver((mutations) => {
|
|
622
540
|
for (const mutation of mutations) {
|
|
@@ -632,38 +550,16 @@ function createDynamicNotificationLiquid(canvas, options = {}) {
|
|
|
632
550
|
childList: true,
|
|
633
551
|
subtree: true
|
|
634
552
|
});
|
|
635
|
-
const intersectionObserver = new IntersectionObserver((entries) => {
|
|
636
|
-
for (const entry of entries) intersecting = entry.isIntersecting;
|
|
637
|
-
invalidate();
|
|
638
|
-
}, { rootMargin: "64px" });
|
|
639
|
-
intersectionObserver.observe(canvas);
|
|
640
|
-
const motionObserver = new MutationObserver(() => {
|
|
641
|
-
if (!staticFrameDrawn || !reducedMotion()) invalidate();
|
|
642
|
-
});
|
|
643
|
-
motionObserver.observe(document.documentElement, {
|
|
644
|
-
attributes: true,
|
|
645
|
-
attributeFilter: ["data-motion"]
|
|
646
|
-
});
|
|
647
|
-
function handleMotionPreference() {
|
|
648
|
-
invalidate();
|
|
649
|
-
}
|
|
650
|
-
document.addEventListener("visibilitychange", handleVisibility);
|
|
651
|
-
reducedMotionQuery.addEventListener("change", handleMotionPreference);
|
|
652
553
|
canvas.addEventListener("webglcontextlost", handleContextLost);
|
|
653
554
|
canvas.addEventListener("webglcontextrestored", handleContextRestored);
|
|
654
555
|
recapture();
|
|
655
|
-
invalidate();
|
|
556
|
+
loop.invalidate();
|
|
656
557
|
return () => {
|
|
657
558
|
destroyed = true;
|
|
658
|
-
|
|
559
|
+
loop.destroy();
|
|
659
560
|
window.clearTimeout(captureTimer);
|
|
660
|
-
resizeObserver.disconnect();
|
|
661
561
|
sceneResizeObserver.disconnect();
|
|
662
562
|
sceneMutationObserver.disconnect();
|
|
663
|
-
intersectionObserver.disconnect();
|
|
664
|
-
motionObserver.disconnect();
|
|
665
|
-
document.removeEventListener("visibilitychange", handleVisibility);
|
|
666
|
-
reducedMotionQuery.removeEventListener("change", handleMotionPreference);
|
|
667
563
|
canvas.removeEventListener("webglcontextlost", handleContextLost);
|
|
668
564
|
canvas.removeEventListener("webglcontextrestored", handleContextRestored);
|
|
669
565
|
delete canvas.dataset.glassReady;
|