@agent-native/toolkit 0.21.3 → 0.22.0-nightly-20260926111339

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/agent-native.eject.json +11 -20
  2. package/dist/agentkit.d.ts +5 -1
  3. package/dist/agentkit.d.ts.map +1 -1
  4. package/dist/agentkit.js +2 -0
  5. package/dist/agentkit.js.map +1 -1
  6. package/dist/app-shell/PromptHome.d.ts +26 -0
  7. package/dist/app-shell/PromptHome.d.ts.map +1 -0
  8. package/dist/app-shell/PromptHome.js +19 -0
  9. package/dist/app-shell/PromptHome.js.map +1 -0
  10. package/dist/app-shell/PromptHome.spec.d.ts +2 -0
  11. package/dist/app-shell/PromptHome.spec.d.ts.map +1 -0
  12. package/dist/app-shell/PromptHome.spec.js +206 -0
  13. package/dist/app-shell/PromptHome.spec.js.map +1 -0
  14. package/dist/app-shell/TemplateLibraryGrid.d.ts +48 -0
  15. package/dist/app-shell/TemplateLibraryGrid.d.ts.map +1 -0
  16. package/dist/app-shell/TemplateLibraryGrid.js +20 -0
  17. package/dist/app-shell/TemplateLibraryGrid.js.map +1 -0
  18. package/dist/app-shell/TemplatePreviewDialog.d.ts +41 -0
  19. package/dist/app-shell/TemplatePreviewDialog.d.ts.map +1 -0
  20. package/dist/app-shell/TemplatePreviewDialog.js +50 -0
  21. package/dist/app-shell/TemplatePreviewDialog.js.map +1 -0
  22. package/dist/app-shell/TemplatePreviewDialog.spec.d.ts +2 -0
  23. package/dist/app-shell/TemplatePreviewDialog.spec.d.ts.map +1 -0
  24. package/dist/app-shell/TemplatePreviewDialog.spec.js +258 -0
  25. package/dist/app-shell/TemplatePreviewDialog.spec.js.map +1 -0
  26. package/dist/app-shell/index.d.ts +3 -0
  27. package/dist/app-shell/index.d.ts.map +1 -1
  28. package/dist/app-shell/index.js +3 -0
  29. package/dist/app-shell/index.js.map +1 -1
  30. package/dist/app-shell/prompt-home.css +159 -0
  31. package/dist/composer/ComposerContextMenu.d.ts +48 -0
  32. package/dist/composer/ComposerContextMenu.d.ts.map +1 -0
  33. package/dist/composer/ComposerContextMenu.js +295 -0
  34. package/dist/composer/ComposerContextMenu.js.map +1 -0
  35. package/dist/composer/ComposerContextMenu.spec.d.ts +2 -0
  36. package/dist/composer/ComposerContextMenu.spec.d.ts.map +1 -0
  37. package/dist/composer/ComposerContextMenu.spec.js +270 -0
  38. package/dist/composer/ComposerContextMenu.spec.js.map +1 -0
  39. package/dist/composer/ComposerContextMenuSearch.d.ts +11 -0
  40. package/dist/composer/ComposerContextMenuSearch.d.ts.map +1 -0
  41. package/dist/composer/ComposerContextMenuSearch.js +42 -0
  42. package/dist/composer/ComposerContextMenuSearch.js.map +1 -0
  43. package/dist/composer/ComposerContextPicker.d.ts +7 -0
  44. package/dist/composer/ComposerContextPicker.d.ts.map +1 -0
  45. package/dist/composer/ComposerContextPicker.js +71 -0
  46. package/dist/composer/ComposerContextPicker.js.map +1 -0
  47. package/dist/composer/ComposerContextPicker.spec.d.ts +2 -0
  48. package/dist/composer/ComposerContextPicker.spec.d.ts.map +1 -0
  49. package/dist/composer/ComposerContextPicker.spec.js +454 -0
  50. package/dist/composer/ComposerContextPicker.spec.js.map +1 -0
  51. package/dist/composer/ComposerContextPickerDialog.d.ts +8 -0
  52. package/dist/composer/ComposerContextPickerDialog.d.ts.map +1 -0
  53. package/dist/composer/ComposerContextPickerDialog.js +101 -0
  54. package/dist/composer/ComposerContextPickerDialog.js.map +1 -0
  55. package/dist/composer/ComposerContextPickerDialog.spec.d.ts +2 -0
  56. package/dist/composer/ComposerContextPickerDialog.spec.d.ts.map +1 -0
  57. package/dist/composer/ComposerContextPickerDialog.spec.js +329 -0
  58. package/dist/composer/ComposerContextPickerDialog.spec.js.map +1 -0
  59. package/dist/composer/ComposerContextSearchInput.d.ts +7 -0
  60. package/dist/composer/ComposerContextSearchInput.d.ts.map +1 -0
  61. package/dist/composer/ComposerContextSearchInput.js +18 -0
  62. package/dist/composer/ComposerContextSearchInput.js.map +1 -0
  63. package/dist/composer/PromptComposer.context.spec.d.ts +2 -0
  64. package/dist/composer/PromptComposer.context.spec.d.ts.map +1 -0
  65. package/dist/composer/PromptComposer.context.spec.js +383 -0
  66. package/dist/composer/PromptComposer.context.spec.js.map +1 -0
  67. package/dist/composer/PromptComposer.d.ts +19 -5
  68. package/dist/composer/PromptComposer.d.ts.map +1 -1
  69. package/dist/composer/PromptComposer.js +30 -25
  70. package/dist/composer/PromptComposer.js.map +1 -1
  71. package/dist/composer/PromptComposer.spec.js +24 -1
  72. package/dist/composer/PromptComposer.spec.js.map +1 -1
  73. package/dist/composer/TiptapComposer.d.ts +13 -2
  74. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  75. package/dist/composer/TiptapComposer.js +89 -32
  76. package/dist/composer/TiptapComposer.js.map +1 -1
  77. package/dist/composer/context-items.d.ts +12 -0
  78. package/dist/composer/context-items.d.ts.map +1 -0
  79. package/dist/composer/context-items.js +46 -0
  80. package/dist/composer/context-items.js.map +1 -0
  81. package/dist/composer/context-items.spec.d.ts +2 -0
  82. package/dist/composer/context-items.spec.d.ts.map +1 -0
  83. package/dist/composer/context-items.spec.js +43 -0
  84. package/dist/composer/context-items.spec.js.map +1 -0
  85. package/dist/composer/index.d.ts +4 -1
  86. package/dist/composer/index.d.ts.map +1 -1
  87. package/dist/composer/index.js +3 -1
  88. package/dist/composer/index.js.map +1 -1
  89. package/dist/composer/runtime-adapters.d.ts +6 -1
  90. package/dist/composer/runtime-adapters.d.ts.map +1 -1
  91. package/dist/composer/runtime-adapters.js.map +1 -1
  92. package/dist/composer/useComposerContextPicker.d.ts +111 -0
  93. package/dist/composer/useComposerContextPicker.d.ts.map +1 -0
  94. package/dist/composer/useComposerContextPicker.js +299 -0
  95. package/dist/composer/useComposerContextPicker.js.map +1 -0
  96. package/dist/design-system/default-adapter.d.ts.map +1 -1
  97. package/dist/design-system/default-adapter.js +13 -7
  98. package/dist/design-system/default-adapter.js.map +1 -1
  99. package/dist/design-system/default-adapter.spec.js +13 -0
  100. package/dist/design-system/default-adapter.spec.js.map +1 -1
  101. package/dist/design-system/types.d.ts +4 -1
  102. package/dist/design-system/types.d.ts.map +1 -1
  103. package/dist/index.d.ts +0 -1
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +0 -1
  106. package/dist/index.js.map +1 -1
  107. package/dist/styles.css +1 -0
  108. package/dist/ui/command.d.ts +8 -2
  109. package/dist/ui/command.d.ts.map +1 -1
  110. package/dist/ui/command.js +9 -2
  111. package/dist/ui/command.js.map +1 -1
  112. package/dist/ui/command.spec.js +20 -1
  113. package/dist/ui/command.spec.js.map +1 -1
  114. package/package.json +2 -15
  115. package/src/agentkit.ts +26 -0
  116. package/src/app-shell/PromptHome.spec.tsx +404 -0
  117. package/src/app-shell/PromptHome.tsx +111 -0
  118. package/src/app-shell/TemplateLibraryGrid.tsx +212 -0
  119. package/src/app-shell/TemplatePreviewDialog.spec.tsx +473 -0
  120. package/src/app-shell/TemplatePreviewDialog.tsx +224 -0
  121. package/src/app-shell/index.ts +3 -0
  122. package/src/app-shell/prompt-home.css +159 -0
  123. package/src/composer/ComposerContextMenu.spec.tsx +343 -0
  124. package/src/composer/ComposerContextMenu.tsx +610 -0
  125. package/src/composer/ComposerContextMenuSearch.tsx +69 -0
  126. package/src/composer/ComposerContextPicker.spec.tsx +520 -0
  127. package/src/composer/ComposerContextPicker.tsx +280 -0
  128. package/src/composer/ComposerContextPickerDialog.spec.tsx +405 -0
  129. package/src/composer/ComposerContextPickerDialog.tsx +274 -0
  130. package/src/composer/ComposerContextSearchInput.tsx +48 -0
  131. package/src/composer/PromptComposer.context.spec.tsx +577 -0
  132. package/src/composer/PromptComposer.spec.ts +24 -1
  133. package/src/composer/PromptComposer.tsx +60 -26
  134. package/src/composer/TiptapComposer.tsx +198 -48
  135. package/src/composer/context-items.spec.ts +68 -0
  136. package/src/composer/context-items.ts +72 -0
  137. package/src/composer/index.ts +27 -0
  138. package/src/composer/runtime-adapters.tsx +6 -1
  139. package/src/composer/useComposerContextPicker.ts +441 -0
  140. package/src/design-system/default-adapter.spec.tsx +23 -0
  141. package/src/design-system/default-adapter.tsx +52 -23
  142. package/src/design-system/types.ts +4 -1
  143. package/src/index.ts +0 -1
  144. package/src/styles.css +1 -0
  145. package/src/ui/command.spec.tsx +33 -1
  146. package/src/ui/command.tsx +52 -9
  147. package/dist/marketing/MarketingHome.d.ts +0 -36
  148. package/dist/marketing/MarketingHome.d.ts.map +0 -1
  149. package/dist/marketing/MarketingHome.js +0 -28
  150. package/dist/marketing/MarketingHome.js.map +0 -1
  151. package/dist/marketing/MarketingHome.spec.d.ts +0 -2
  152. package/dist/marketing/MarketingHome.spec.d.ts.map +0 -1
  153. package/dist/marketing/MarketingHome.spec.js +0 -44
  154. package/dist/marketing/MarketingHome.spec.js.map +0 -1
  155. package/dist/marketing/Starfield.d.ts +0 -8
  156. package/dist/marketing/Starfield.d.ts.map +0 -1
  157. package/dist/marketing/Starfield.js +0 -349
  158. package/dist/marketing/Starfield.js.map +0 -1
  159. package/dist/marketing/index.d.ts +0 -3
  160. package/dist/marketing/index.d.ts.map +0 -1
  161. package/dist/marketing/index.js +0 -3
  162. package/dist/marketing/index.js.map +0 -1
  163. package/src/marketing/MarketingHome.spec.tsx +0 -75
  164. package/src/marketing/MarketingHome.tsx +0 -183
  165. package/src/marketing/Starfield.tsx +0 -403
  166. package/src/marketing/index.ts +0 -6
@@ -1,75 +0,0 @@
1
- import { renderToStaticMarkup } from "react-dom/server";
2
- import { describe, expect, it } from "vitest";
3
-
4
- import { MarketingHome } from "./MarketingHome.js";
5
- import { Starfield } from "./Starfield.js";
6
-
7
- describe("MarketingHome", () => {
8
- it("renders the default marketing shell and action links on the server", () => {
9
- const html = renderToStaticMarkup(
10
- <MarketingHome
11
- appName="Example"
12
- tagline="Build something useful"
13
- description="A public description"
14
- valueProps={[{ title: "Reusable", description: "By default" }]}
15
- primaryActionHref="/home"
16
- secondaryActionHref="/sign-in"
17
- />,
18
- );
19
-
20
- expect(html).toContain('data-agent-native-marketing-home="true"');
21
- expect(html).toContain("Build something useful");
22
- expect(html).toContain('href="/home"');
23
- expect(html).toContain("Open Example");
24
- expect(html).toContain('href="/sign-in"');
25
- expect(html).toContain("Sign in");
26
- expect(html).toContain("Reusable");
27
- expect(html).toContain("max-w-7xl");
28
- expect(html).toContain("py-16");
29
- expect(html).toContain('data-agent-native-starfield="true"');
30
- expect(html).not.toContain("max-w-6xl");
31
- });
32
-
33
- it("allows a route to replace the hero while retaining the public shell", () => {
34
- const html = renderToStaticMarkup(
35
- <MarketingHome appName="Example" background={<canvas />}>
36
- <div>Custom hero</div>
37
- </MarketingHome>,
38
- );
39
-
40
- expect(html).toContain("Custom hero");
41
- expect(html).not.toContain("Example</p>");
42
- expect(html).toContain("<canvas");
43
- });
44
-
45
- it("provides an opt-in auth composition without changing the default shell", () => {
46
- const html = renderToStaticMarkup(
47
- <MarketingHome
48
- appName="Example"
49
- variant="auth"
50
- background={<Starfield id="auth-starfield" />}
51
- auth={<div>Sign in form</div>}
52
- >
53
- <div>Marketing copy</div>
54
- </MarketingHome>,
55
- );
56
-
57
- expect(html).toContain("Marketing copy");
58
- expect(html).toContain("Sign in form");
59
- expect(html).toContain('id="auth-starfield"');
60
- expect(html).toContain("max-w-6xl");
61
- expect(html).toContain("auth-marketing-layout");
62
- expect(html).toContain("max-w-md");
63
- expect(html.indexOf("Sign in form")).toBeLessThan(
64
- html.indexOf("Marketing copy"),
65
- );
66
- });
67
-
68
- it("renders the starfield canvas without browser APIs during SSR", () => {
69
- const html = renderToStaticMarkup(<Starfield />);
70
-
71
- expect(html).toContain('<canvas id="starfield"');
72
- expect(html).toContain('aria-hidden="true"');
73
- expect(html).toContain('data-agent-native-starfield="true"');
74
- });
75
- });
@@ -1,183 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { Button } from "../ui/button.js";
4
- import { cn } from "../utils.js";
5
- import { Starfield } from "./Starfield.js";
6
-
7
- export interface MarketingValueProp {
8
- title: React.ReactNode;
9
- description?: React.ReactNode;
10
- }
11
-
12
- export interface MarketingHomeProps {
13
- /** Product or app name shown in the public shell. */
14
- appName: React.ReactNode;
15
- /** Choose the standard public shell or the split auth composition. */
16
- variant?: "default" | "auth";
17
- /** Main value proposition. */
18
- tagline?: React.ReactNode;
19
- /** Supporting product description. */
20
- description?: React.ReactNode;
21
- /** Short value props rendered below the hero. */
22
- valueProps?: readonly (MarketingValueProp | React.ReactNode)[];
23
- /** Optional visual layer, including a 3D/canvas background. */
24
- background?: React.ReactNode;
25
- /** Primary and secondary calls to action. */
26
- primaryAction?: React.ReactNode;
27
- secondaryAction?: React.ReactNode;
28
- /** Optional page-level action rendered above the auth split. */
29
- topRight?: React.ReactNode;
30
- /** Convenience links for the default primary and secondary buttons. */
31
- primaryActionHref?: string;
32
- secondaryActionHref?: string;
33
- primaryActionLabel?: React.ReactNode;
34
- secondaryActionLabel?: React.ReactNode;
35
- /** Optional auth or signup surface beside the marketing copy. */
36
- auth?: React.ReactNode;
37
- /** Replace the default hero entirely while retaining the page shell. */
38
- children?: React.ReactNode;
39
- className?: string;
40
- }
41
-
42
- export function MarketingHome({
43
- appName,
44
- variant = "default",
45
- tagline,
46
- description,
47
- valueProps = [],
48
- background = <Starfield />,
49
- primaryAction,
50
- secondaryAction,
51
- topRight,
52
- auth,
53
- children,
54
- className,
55
- primaryActionHref,
56
- secondaryActionHref,
57
- primaryActionLabel,
58
- secondaryActionLabel,
59
- }: MarketingHomeProps) {
60
- const isAuthVariant = variant === "auth";
61
- const resolvedPrimaryAction =
62
- primaryAction ??
63
- (primaryActionHref ? (
64
- <Button asChild size="lg">
65
- <a href={primaryActionHref}>
66
- {primaryActionLabel ?? <>Open {appName}</>}
67
- </a>
68
- </Button>
69
- ) : null);
70
- const resolvedSecondaryAction =
71
- secondaryAction ??
72
- (secondaryActionHref ? (
73
- <Button asChild size="lg" variant="outline">
74
- <a href={secondaryActionHref}>{secondaryActionLabel ?? "Sign in"}</a>
75
- </Button>
76
- ) : null);
77
-
78
- const content = children ?? (
79
- <>
80
- <p className="text-sm font-medium tracking-[0.18em] text-primary uppercase">
81
- {appName}
82
- </p>
83
- <h1 className="mt-5 max-w-3xl text-4xl font-semibold tracking-tight text-foreground sm:text-6xl">
84
- {tagline ?? appName}
85
- </h1>
86
- {description ? (
87
- <p className="mt-6 max-w-2xl text-base leading-7 text-muted-foreground sm:text-lg">
88
- {description}
89
- </p>
90
- ) : null}
91
- {valueProps.length > 0 ? (
92
- <div className="mt-10 grid gap-4 sm:grid-cols-2">
93
- {valueProps.map((valueProp, index) => {
94
- const isStructured =
95
- typeof valueProp === "object" &&
96
- valueProp !== null &&
97
- "title" in valueProp;
98
- const title = isStructured
99
- ? (valueProp as MarketingValueProp).title
100
- : valueProp;
101
- const valueDescription = isStructured
102
- ? (valueProp as MarketingValueProp).description
103
- : undefined;
104
- return (
105
- <div
106
- key={index}
107
- className="rounded-xl border border-border/60 bg-background/70 p-4 backdrop-blur-sm"
108
- >
109
- <p className="text-sm font-medium text-foreground">{title}</p>
110
- {valueDescription ? (
111
- <p className="mt-1 text-sm leading-6 text-muted-foreground">
112
- {valueDescription}
113
- </p>
114
- ) : null}
115
- </div>
116
- );
117
- })}
118
- </div>
119
- ) : null}
120
- {resolvedPrimaryAction || resolvedSecondaryAction ? (
121
- <div className="mt-10 flex flex-wrap items-center gap-3">
122
- {resolvedPrimaryAction}
123
- {resolvedSecondaryAction}
124
- </div>
125
- ) : null}
126
- </>
127
- );
128
- const authPanel = auth ? (
129
- <aside
130
- className={cn(
131
- isAuthVariant ? "form-panel w-full max-w-md justify-self-end" : "",
132
- )}
133
- >
134
- {auth}
135
- </aside>
136
- ) : null;
137
-
138
- return (
139
- <main
140
- className={cn(
141
- "relative isolate min-h-screen overflow-hidden bg-background text-foreground",
142
- className,
143
- )}
144
- data-agent-native-marketing-home
145
- >
146
- {background ? (
147
- <div className="pointer-events-none absolute inset-0 -z-10">
148
- {background}
149
- </div>
150
- ) : null}
151
- <div
152
- className={cn(
153
- isAuthVariant
154
- ? cn(
155
- "auth-marketing-shell mx-auto flex min-h-screen w-full items-center px-6 py-10 sm:px-10 lg:px-16",
156
- topRight ? "auth-marketing-shell-with-top-right" : "",
157
- )
158
- : "mx-auto flex min-h-screen w-full max-w-7xl items-center px-6 py-16 sm:px-10 lg:px-16",
159
- )}
160
- >
161
- {isAuthVariant && topRight ? (
162
- <div className="auth-marketing-top-right">{topRight}</div>
163
- ) : null}
164
- <div
165
- className={cn(
166
- isAuthVariant && auth ? "split" : "grid w-full items-center gap-12",
167
- auth ? "lg:grid-cols-[minmax(0,1fr)_minmax(20rem,28rem)]" : "",
168
- isAuthVariant && auth ? "max-w-6xl" : "",
169
- isAuthVariant && auth ? "auth-marketing-layout" : "",
170
- )}
171
- >
172
- {isAuthVariant ? authPanel : null}
173
- <section
174
- className={isAuthVariant && auth ? "marketing-panel" : undefined}
175
- >
176
- {content}
177
- </section>
178
- {!isAuthVariant ? authPanel : null}
179
- </div>
180
- </div>
181
- </main>
182
- );
183
- }
@@ -1,403 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "../utils.js";
4
-
5
- const VERTEX_SHADER_SOURCE = `
6
- attribute vec2 position;
7
- void main() {
8
- gl_Position = vec4(position, 0.0, 1.0);
9
- }
10
- `;
11
-
12
- const FRAGMENT_SHADER_SOURCE = `
13
- precision highp float;
14
-
15
- uniform float iTime;
16
- uniform vec2 iResolution;
17
- uniform vec3 uPointer;
18
- uniform vec3 uFgColor;
19
- uniform vec3 uBgColor;
20
- uniform float uBrightness;
21
-
22
- #define S(a, b, t) smoothstep(a, b, t)
23
-
24
- const float DOT_DENSITY = 220.;
25
- const float DOT_SCALE = 0.9;
26
- const float SPEED = 0.65;
27
-
28
- float N21(vec2 p) {
29
- vec3 a = fract(vec3(p.xyx) * vec3(213.897, 653.453, 253.098));
30
- a += dot(a, a.yzx + 79.76);
31
- return fract((a.x + a.y) * a.z);
32
- }
33
-
34
- float valueNoise(vec2 p) {
35
- vec2 cell = floor(p);
36
- vec2 f = fract(p);
37
- float a = N21(cell);
38
- float b = N21(cell + vec2(1., 0.));
39
- float c = N21(cell + vec2(0., 1.));
40
- float d = N21(cell + vec2(1., 1.));
41
- vec2 u = f * f * (3. - 2. * f);
42
- return mix(a, b, u.x) + (c - a) * u.y * (1. - u.x) + (d - b) * u.x * u.y;
43
- }
44
-
45
- float waveHeight(float x, float time) {
46
- float wave = sin(x * 1.7 + time * 0.25) * 0.06;
47
- wave += sin(x * 4.3 - time * 0.5) * 0.028;
48
- wave += sin(x * 10.5 + time * 0.8) * 0.012;
49
- wave += (valueNoise(vec2(x * 2.2 + time * 0.06, 4.)) - 0.5) * 0.05;
50
- return -0.21 + wave;
51
- }
52
-
53
- float waveProfile(vec2 point, vec2 pointerUv, float pointerStrength, float time) {
54
- float center = waveHeight(point.x, time);
55
- vec2 pointerDelta = (point - pointerUv) * vec2(0.75, 1.15);
56
- float pointerPull = pointerStrength * exp(-dot(pointerDelta, pointerDelta) * 2.8);
57
- center += (pointerUv.y - center) * pointerPull * 0.42;
58
- center += sin((point.x - pointerUv.x) * 8.0 + time) * pointerPull * 0.035;
59
-
60
- float distance = point.y - center;
61
- float profile = exp(-pow(distance / 0.065, 2.));
62
- profile += 0.48 * exp(-pow((distance + 0.09) / 0.12, 2.));
63
- profile += 0.28 * exp(-pow((distance - 0.14) / 0.16, 2.));
64
- profile *= 0.35 + 0.65 * valueNoise(vec2(point.x * 5.0 + time * 0.08, point.y * 14.0 - time * 0.04));
65
- return clamp(profile, 0., 1.);
66
- }
67
-
68
- void mainImage(out vec4 fragColor, in vec2 fragCoord) {
69
- vec2 uv = (fragCoord - iResolution.xy * 0.5) / iResolution.y;
70
- vec2 pointerUv = (uPointer.xy - iResolution.xy * 0.5) / iResolution.y;
71
- float time = iTime * SPEED;
72
-
73
- float cellSize = 1. / DOT_DENSITY;
74
- vec2 cell = floor(uv / cellSize);
75
- vec2 jitter = vec2(
76
- N21(cell + vec2(11.7, 2.3)),
77
- N21(cell + vec2(-5.2, 8.1))
78
- ) - 0.5;
79
- vec2 cellCenter = (cell + 0.5 + jitter * 0.7) * cellSize;
80
- vec2 cellUv = (uv - cellCenter) / cellSize;
81
-
82
- float tone = waveProfile(cellCenter, pointerUv, uPointer.z, time);
83
- float grain = 0.38 + 0.62 * N21(cell + vec2(17.3, -4.1));
84
- float dropout = S(0.45, 0.95, grain + tone * 0.8);
85
- tone *= grain * dropout;
86
-
87
- float radius = sqrt(tone) * 0.5 * DOT_SCALE;
88
- float pixel = (1. / iResolution.y) / cellSize;
89
- float edge = max(pixel * 1.2, 0.004);
90
- float distance = length(cellUv);
91
- float dotMask = step(0.0001, tone) * (1. - S(radius - edge, radius + edge, distance));
92
- float glow = (1. - S(radius, radius + 0.7, distance)) * tone * 0.14;
93
- float value = clamp(dotMask + glow, 0., 1.);
94
-
95
- vec3 lit = mix(uBgColor, uFgColor, value);
96
- lit += (uFgColor - uBgColor) * value * tone * (uBrightness - 1.);
97
- fragColor = vec4(clamp(lit, 0., 1.), 1.);
98
- }
99
-
100
- void main() {
101
- mainImage(gl_FragColor, gl_FragCoord.xy);
102
- }
103
- `;
104
-
105
- type ThemeColors = {
106
- bg: readonly [number, number, number];
107
- fg: readonly [number, number, number];
108
- brightness: number;
109
- };
110
-
111
- const DARK_THEME: ThemeColors = {
112
- bg: [0, 0, 0],
113
- fg: [174 / 255, 173 / 255, 172 / 255],
114
- brightness: 1.6,
115
- };
116
-
117
- const LIGHT_THEME: ThemeColors = {
118
- bg: [250 / 255, 249 / 255, 245 / 255],
119
- fg: [61 / 255, 61 / 255, 61 / 255],
120
- brightness: 1,
121
- };
122
-
123
- function isDarkMode(): boolean {
124
- const root = document.documentElement;
125
- if (root.classList.contains("dark")) return true;
126
- if (root.classList.contains("light")) return false;
127
- const dataTheme = root.getAttribute("data-theme");
128
- if (dataTheme === "dark") return true;
129
- if (dataTheme === "light") return false;
130
- return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;
131
- }
132
-
133
- function createShader(
134
- gl: WebGLRenderingContext,
135
- type: number,
136
- source: string,
137
- ): WebGLShader | null {
138
- const shader = gl.createShader(type);
139
- if (!shader) return null;
140
- gl.shaderSource(shader, source);
141
- gl.compileShader(shader);
142
- if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
143
- gl.deleteShader(shader);
144
- return null;
145
- }
146
- return shader;
147
- }
148
-
149
- function initializeStarfield(
150
- canvas: HTMLCanvasElement,
151
- frameRate: number,
152
- ): () => void {
153
- let gl: WebGLRenderingContext | null = null;
154
- try {
155
- gl = canvas.getContext("webgl", { alpha: false, antialias: false });
156
- } catch {
157
- return () => undefined;
158
- }
159
- if (!gl) return () => undefined;
160
-
161
- const vertexShader = createShader(gl, gl.VERTEX_SHADER, VERTEX_SHADER_SOURCE);
162
- const fragmentShader = createShader(
163
- gl,
164
- gl.FRAGMENT_SHADER,
165
- FRAGMENT_SHADER_SOURCE,
166
- );
167
- if (!vertexShader || !fragmentShader) {
168
- gl.deleteShader(vertexShader);
169
- gl.deleteShader(fragmentShader);
170
- return () => undefined;
171
- }
172
-
173
- const program = gl.createProgram();
174
- if (!program) {
175
- gl.deleteShader(vertexShader);
176
- gl.deleteShader(fragmentShader);
177
- return () => undefined;
178
- }
179
- gl.attachShader(program, vertexShader);
180
- gl.attachShader(program, fragmentShader);
181
- gl.linkProgram(program);
182
- if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
183
- gl.deleteProgram(program);
184
- gl.deleteShader(vertexShader);
185
- gl.deleteShader(fragmentShader);
186
- return () => undefined;
187
- }
188
-
189
- const buffer = gl.createBuffer();
190
- const position = gl.getAttribLocation(program, "position");
191
- if (!buffer || position < 0) {
192
- gl.deleteBuffer(buffer);
193
- gl.deleteProgram(program);
194
- gl.deleteShader(vertexShader);
195
- gl.deleteShader(fragmentShader);
196
- return () => undefined;
197
- }
198
-
199
- gl.useProgram(program);
200
- gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
201
- gl.bufferData(
202
- gl.ARRAY_BUFFER,
203
- new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),
204
- gl.STATIC_DRAW,
205
- );
206
- gl.enableVertexAttribArray(position);
207
- gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
208
-
209
- const timeUniform = gl.getUniformLocation(program, "iTime");
210
- const resolutionUniform = gl.getUniformLocation(program, "iResolution");
211
- const pointerUniform = gl.getUniformLocation(program, "uPointer");
212
- const fgColorUniform = gl.getUniformLocation(program, "uFgColor");
213
- const bgColorUniform = gl.getUniformLocation(program, "uBgColor");
214
- const brightnessUniform = gl.getUniformLocation(program, "uBrightness");
215
- const reducedMotionQuery = window.matchMedia?.(
216
- "(prefers-reduced-motion: reduce)",
217
- );
218
- let reducedMotion = reducedMotionQuery?.matches ?? false;
219
- let devicePixelRatio = 1;
220
- let hasPointer = false;
221
- let pointerX = 0;
222
- let pointerY = 0;
223
- let pointerStrength = 0;
224
- let targetX = 0;
225
- let targetY = 0;
226
- let targetStrength = 0;
227
- let animationFrame = 0;
228
- let theme = isDarkMode() ? DARK_THEME : LIGHT_THEME;
229
-
230
- const resize = () => {
231
- const rect = canvas.getBoundingClientRect();
232
- const width = Math.max(1, rect.width || window.innerWidth);
233
- const height = Math.max(1, rect.height || window.innerHeight);
234
- devicePixelRatio = Math.min(window.devicePixelRatio || 1, 1.5);
235
- canvas.width = Math.floor(width * devicePixelRatio);
236
- canvas.height = Math.floor(height * devicePixelRatio);
237
- gl?.viewport(0, 0, canvas.width, canvas.height);
238
- if (!hasPointer) {
239
- pointerX = targetX = canvas.width * 0.5;
240
- pointerY = targetY = canvas.height * 0.5;
241
- }
242
- };
243
-
244
- const onPointerMove = (event: Event) => {
245
- const mouseEvent = event as MouseEvent;
246
- const rect = canvas.getBoundingClientRect();
247
- const x = mouseEvent.clientX - rect.left;
248
- const y = mouseEvent.clientY - rect.top;
249
- hasPointer = true;
250
- targetX = x * devicePixelRatio;
251
- targetY = (rect.height - y) * devicePixelRatio;
252
- targetStrength =
253
- x >= 0 && x <= rect.width && y >= 0 && y <= rect.height ? 1 : 0;
254
- };
255
- const fadePointer = () => {
256
- targetStrength = 0;
257
- };
258
- const easePointer = (allowPointer: boolean) => {
259
- if (!allowPointer) {
260
- pointerStrength = 0;
261
- return;
262
- }
263
- pointerX += (targetX - pointerX) * 0.22;
264
- pointerY += (targetY - pointerY) * 0.22;
265
- pointerStrength += (targetStrength - pointerStrength) * 0.14;
266
- if (pointerStrength < 0.001 && targetStrength === 0) pointerStrength = 0;
267
- };
268
- const draw = (timeSeconds: number, allowPointer: boolean) => {
269
- easePointer(allowPointer && !reducedMotion);
270
- gl?.uniform1f(timeUniform, timeSeconds);
271
- gl?.uniform2f(resolutionUniform, canvas.width, canvas.height);
272
- gl?.uniform3f(pointerUniform, pointerX, pointerY, pointerStrength);
273
- gl?.uniform3f(fgColorUniform, theme.fg[0], theme.fg[1], theme.fg[2]);
274
- gl?.uniform3f(bgColorUniform, theme.bg[0], theme.bg[1], theme.bg[2]);
275
- gl?.uniform1f(brightnessUniform, theme.brightness);
276
- gl?.drawArrays(gl.TRIANGLES, 0, 6);
277
- };
278
-
279
- const listenerOptions: AddEventListenerOptions = { passive: true };
280
- const resizeObserver =
281
- typeof ResizeObserver === "undefined"
282
- ? undefined
283
- : new ResizeObserver(resize);
284
- resizeObserver?.observe(canvas);
285
- window.addEventListener("resize", resize);
286
- window.addEventListener("pointermove", onPointerMove, listenerOptions);
287
- window.addEventListener("mousemove", onPointerMove, listenerOptions);
288
- document.addEventListener("pointerleave", fadePointer, listenerOptions);
289
- window.addEventListener("blur", fadePointer);
290
-
291
- let start = performance.now();
292
- let last = 0;
293
- const frameBudget = 1000 / Math.max(1, frameRate);
294
- const render = (now: number) => {
295
- if (reducedMotion) {
296
- animationFrame = 0;
297
- return;
298
- }
299
- animationFrame = requestAnimationFrame(render);
300
- if (now - last < frameBudget) return;
301
- last = now;
302
- draw((now - start) * 0.001, true);
303
- };
304
- const startAnimation = () => {
305
- if (!animationFrame) animationFrame = requestAnimationFrame(render);
306
- };
307
- const stopAnimation = () => {
308
- if (animationFrame) {
309
- cancelAnimationFrame(animationFrame);
310
- animationFrame = 0;
311
- }
312
- };
313
- const onReducedMotionChange = () => {
314
- reducedMotion = reducedMotionQuery?.matches ?? false;
315
- if (reducedMotion) {
316
- stopAnimation();
317
- last = 0;
318
- draw(20, false);
319
- } else {
320
- start = performance.now();
321
- startAnimation();
322
- }
323
- };
324
- const themeObserver = new MutationObserver(() => {
325
- theme = isDarkMode() ? DARK_THEME : LIGHT_THEME;
326
- if (reducedMotion) draw(20, false);
327
- });
328
- themeObserver.observe(document.documentElement, {
329
- attributes: true,
330
- attributeFilter: ["class", "data-theme"],
331
- });
332
-
333
- resize();
334
- draw(reducedMotion ? 20 : 0, !reducedMotion);
335
- if (reducedMotionQuery) {
336
- if (reducedMotionQuery.addEventListener) {
337
- reducedMotionQuery.addEventListener("change", onReducedMotionChange);
338
- } else {
339
- reducedMotionQuery.addListener(onReducedMotionChange);
340
- }
341
- }
342
- if (!reducedMotion) startAnimation();
343
-
344
- return () => {
345
- stopAnimation();
346
- resizeObserver?.disconnect();
347
- themeObserver.disconnect();
348
- window.removeEventListener("resize", resize);
349
- window.removeEventListener("pointermove", onPointerMove, listenerOptions);
350
- window.removeEventListener("mousemove", onPointerMove, listenerOptions);
351
- document.removeEventListener("pointerleave", fadePointer, listenerOptions);
352
- window.removeEventListener("blur", fadePointer);
353
- if (reducedMotionQuery) {
354
- if (reducedMotionQuery.removeEventListener) {
355
- reducedMotionQuery.removeEventListener("change", onReducedMotionChange);
356
- } else {
357
- reducedMotionQuery.removeListener(onReducedMotionChange);
358
- }
359
- }
360
- gl?.deleteBuffer(buffer);
361
- gl?.deleteProgram(program);
362
- gl?.deleteShader(vertexShader);
363
- gl?.deleteShader(fragmentShader);
364
- };
365
- }
366
-
367
- export interface StarfieldProps extends Omit<
368
- React.ComponentPropsWithoutRef<"canvas">,
369
- "children"
370
- > {
371
- /** Keep the default id for compatibility with existing starfield styles. */
372
- id?: string;
373
- frameRate?: number;
374
- }
375
-
376
- export function Starfield({
377
- className,
378
- id = "starfield",
379
- frameRate = 30,
380
- ...props
381
- }: StarfieldProps) {
382
- const canvasRef = React.useRef<HTMLCanvasElement>(null);
383
-
384
- React.useEffect(() => {
385
- const canvas = canvasRef.current;
386
- if (!canvas) return;
387
- return initializeStarfield(canvas, frameRate);
388
- }, [frameRate]);
389
-
390
- return (
391
- <canvas
392
- {...props}
393
- ref={canvasRef}
394
- id={id}
395
- aria-hidden="true"
396
- className={cn(
397
- "pointer-events-none block h-full w-full opacity-[0.35] motion-reduce:opacity-[0.18]",
398
- className,
399
- )}
400
- data-agent-native-starfield
401
- />
402
- );
403
- }
@@ -1,6 +0,0 @@
1
- export {
2
- MarketingHome,
3
- type MarketingHomeProps,
4
- type MarketingValueProp,
5
- } from "./MarketingHome.js";
6
- export { Starfield, type StarfieldProps } from "./Starfield.js";