@q9labsai/chalk-react 5.1.0 → 5.2.0

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 (38) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/dist/bindings/hooks.d.ts +2 -0
  3. package/dist/{chunk-QDEKMRH2.js → chunk-CYC2G42K.js} +66 -58
  4. package/dist/{chunk-XOHNIYSR.js → chunk-G62BKRNX.js} +1534 -948
  5. package/dist/{chunk-ZFWIMKUC.js → chunk-HWQLW4JR.js} +55 -7
  6. package/dist/components/board/BoardMenu.d.ts +4 -1
  7. package/dist/components/board/board-draw-policy.d.ts +15 -0
  8. package/dist/components/board/board-model.d.ts +5 -1
  9. package/dist/components/board/board-view-versions.d.ts +1 -7
  10. package/dist/components/chalk/Chalk.d.ts +4 -0
  11. package/dist/components/chalk/ReportProblemDialog.d.ts +12 -0
  12. package/dist/components/chalk/StatusSurface.d.ts +3 -2
  13. package/dist/components/chalk-ui/ChalkSheet.d.ts +1 -1
  14. package/dist/components/chalk-ui/ChalkToast.d.ts +7 -1
  15. package/dist/components/control-bar/command-failure.d.ts +105 -1
  16. package/dist/components/control-bar/control-bar-behavior.d.ts +5 -1
  17. package/dist/components/index.js +3 -3
  18. package/dist/components/offline-actions/OfflineActions.d.ts +2 -1
  19. package/dist/components/space-header/CallDetails.d.ts +4 -2
  20. package/dist/components/space-header/space-header-contract.d.ts +5 -0
  21. package/dist/components/space-view/SpaceView.d.ts +8 -3
  22. package/dist/components/stage/Stage.d.ts +1 -1
  23. package/dist/components/stage/stage-items.d.ts +4 -0
  24. package/dist/components/stage/stage-layout.d.ts +6 -2
  25. package/dist/components/toast-stack/CallEventIcon.d.ts +1 -1
  26. package/dist/components/toast-stack/NoticeButton.d.ts +7 -0
  27. package/dist/components/toast-stack/ToastStack.d.ts +13 -2
  28. package/dist/components/toast-stack/call-events.d.ts +2 -1
  29. package/dist/components/toast-stack/notice-announcement.d.ts +4 -0
  30. package/dist/components/toast-stack/toast-layout-snapshot.d.ts +4 -0
  31. package/dist/components/toast-stack/useToastCapacity.d.ts +7 -0
  32. package/dist/headless.js +1 -1
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.js +24 -3
  35. package/dist/internal/whiteboard-view-limits.d.ts +1 -0
  36. package/dist/public-surface.contract.d.ts +1 -1
  37. package/dist/test-support/index.js +3 -3
  38. package/package.json +5 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,107 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.2.0
4
+
5
+ ### board-draw-policy
6
+
7
+ Add a call-wide drawing policy for the board: everyone, only hosts, or picked people.
8
+ Hosts set it with `setDrawPolicy` on the whiteboard transport and pick people with the
9
+ existing `setDrawPermission`. Sync computes each person's permission and sends it with
10
+ the policy over the new `draw_policy_v1` extension, so the summary and
11
+ `useWhiteboard().access` now carry `canDraw`, `drawPolicy` and the host's per-person
12
+ choices. The React board follows that permission instead of the role capability, and
13
+ hosts get a "Who can draw" section with a person picker in the board menu.
14
+
15
+ The whiteboard client now steps its hello down only when Sync can't decode it (close
16
+ 1009). A rejected token or a late hello (close 1008) is retried at the same level up to
17
+ four times, so a brief token problem no longer hides the drawing policy for the rest of
18
+ the connection. On a Sync without the drawing policy, a host's waiting policy change is
19
+ refused as `unavailable`, and also reported on the summary, instead of being replayed.
20
+
21
+ ### board-frame-clock
22
+
23
+ Keep Follow active when Bring everyone moves to a wider board view, without a brief zoom overshoot at the start of the animation.
24
+
25
+ ### board-stage-motion
26
+
27
+ Keep the live whiteboard free of stage translation and scaling animations so
28
+ switching between a participant and the board preserves the full canvas and
29
+ accurate drawing coordinates. Other stage tiles keep their existing motion.
30
+
31
+ ### brand-single-chalk-mark
32
+
33
+ Replace the retired colourful four-stick logo with the monochrome single chalk stick and dust in the distributed SVG assets and React Logo variants. Preserve existing asset filenames and component props. The React Logo now takes its default colour from the surrounding text colour.
34
+
35
+ ### follow-view-owner
36
+
37
+ Converge Follow views on one Episode owner, recover from its canonical snapshot,
38
+ and preserve real camera moves during recovery and tab switches. Zoom controls
39
+ and keyboard moves now consistently leave Follow and lead the person's view.
40
+
41
+ Keep all 500 active people's views using internal schema-generated limits in the client and React caches. The client's public bounds, event unions and decoded-frame unions are unchanged. The client now exports `ChalkWhiteboardViewVersions`, which the React SDK uses to order view moves, and whiteboard view events now carry `ownerId`, `ownerSequence` and `originEpoch`, so both releases are minor.
42
+
43
+ ### phone-board-notices-next-release
44
+
45
+ Float faces over a phone board, and keep call notices off the call's controls.
46
+ On a phone, an open board now fills the stage and up to three faces float at
47
+ its right edge, with short name tags. Notices sit between the header and the
48
+ controls under them. A call shorter than 620px, such as a landscape phone or a
49
+ short embedded call, puts every notice in a notice button in the header, which
50
+ lists each one whole and announces it once to screen readers; a taller call
51
+ shows the newest three and puts older ones in the button. A notice in the
52
+ button starts its timer only once it shows. The header stays visible while
53
+ the button holds notices. Long names, links and error tokens wrap inside
54
+ their notice, and a long call name shortens to make room for the timer and
55
+ the notice button. A screen share beside a short board always keeps its own
56
+ tile when there is no explicit cap. A capped strip or floating column counts
57
+ shared content and its overflow button against that cap; folded shares stay
58
+ readable in the shared-content overflow. One text-only announcer handles
59
+ notice speech independently of its stage or header presentation.
60
+
61
+ New optional props: `ToastStackProps.onFold` reports the ids of the notices
62
+ the stage has no room for, `ChalkToastProps.announce` lets a host that already
63
+ announced a notice keep its toast quiet, and `SpaceHeaderProps.notices` places
64
+ a control before the people button.
65
+
66
+ ### problem-reporting
67
+
68
+ Add the problem-report API contract and reusable report dialog, with opt-in diagnostics,
69
+ editable delivery failures and bounded Unicode input. Call details and failed or ended
70
+ call screens can expose reporting when an integration supplies a delivery callback.
71
+ Use `reportUnavailableReason` to show expired access inside an editable report.
72
+ Live report drafts survive call termination. Integrations with their own terminal
73
+ screens can use `onOpenReportProblem` to keep one app-owned dialog across those screens.
74
+ Give `ReportProblemDialog` a `focusScope` containing the current call or terminal screen,
75
+ so closing it returns focus to its usable opener, or to a visible current control
76
+ when the opener has disappeared. Hidden, inert and disabled targets are skipped.
77
+
78
+ ### recording-auto-render
79
+
80
+ Expose recording expiry reasons through additive failure fields while preserving the existing export status enum.
81
+
82
+ Add the Tenant recording_render_mode setting, defaulting to on_demand, with opt-in automatic MP4 rendering.
83
+
84
+ ### safe-media-command-guidance
85
+
86
+ Show safe, source-aware guidance for media command failures across call controls,
87
+ keyboard shortcuts, screen sharing, and media request dialogs. Preserve the
88
+ existing onCommandError MediaSource argument and provide extended media context
89
+ in an optional third argument. Keep disable-failure wording neutral and explain
90
+ unsupported speaker selection without suggesting a retry. Preserve start/stop
91
+ intent in failure guidance and retries; show terminal guidance for withdrawn
92
+ media requests and other non-recoverable failures.
93
+
94
+ Distinguish device selection, speaker tests, and incoming requests in media error
95
+ guidance. Cover every client failure code with an operation-aware policy and only
96
+ offer retry when the exact failed media command can be replayed.
97
+
98
+ Preserve request context in the integrated Chalk dialog and show safe terminal
99
+ guidance without retry for unknown runtime failure codes.
100
+
101
+ ### sync-connect-diagnostics
102
+
103
+ Report Sync connection failures with the existing `connection.sync_hello_timeout` code without adding values to the public `ErrorCode` union. Add the current attempt's safe diagnostics to client errors, telemetry, and the call failure reference: opening/hello stage, close code and bounded reason, whether the socket opened, and elapsed time. Earlier attempts remain in the diagnostic history. Browsers do not expose DNS, TCP, or TLS progress. Unrecognized peer reasons are redacted. This is a compatible patch for the next client and React releases; released 5.1.0 files are unchanged.
104
+
3
105
  ## 5.1.0
4
106
 
5
107
  - Add optional `CallLimbo.className` for the message card, so a host can size the card around a form.
@@ -21,6 +21,8 @@ export declare function useReactions(): ReactionsSlice;
21
21
  export declare function useRecording(): RecordingSlice;
22
22
  export declare function useWhiteboard(): WhiteboardSlice;
23
23
  export declare function useCan(capability: Capability): boolean;
24
+ /** Whether this person can draw on the board. Sync decides from the call's policy, their role and the host's choice for them; until the board subscription is live, the role default stands in. A Sync without the drawing policy doesn't report role changes to the board, so there the live role still limits drawing. */
25
+ export declare function useCanDrawWhiteboard(): boolean;
24
26
  /** Local capture state and the action that opens the browser's share picker. */
25
27
  export declare function useScreenShare(): {
26
28
  source: import("@q9labsai/chalk-client").MediaSource;
@@ -28,17 +28,19 @@ import {
28
28
  StatusSurface,
29
29
  WhiteboardView,
30
30
  chalkThemeStyle,
31
+ commandFailure,
31
32
  useCan,
33
+ useCanDrawWhiteboard,
32
34
  useConnection,
33
35
  useMedia,
34
36
  useRecording,
35
37
  useSpaceClient,
36
38
  useWhiteboard
37
- } from "./chunk-XOHNIYSR.js";
39
+ } from "./chunk-G62BKRNX.js";
38
40
  import {
39
41
  WHITEBOARD_ITEM_ID,
40
42
  screenShareItemId
41
- } from "./chunk-ZFWIMKUC.js";
43
+ } from "./chunk-HWQLW4JR.js";
42
44
  import {
43
45
  Download01Icon,
44
46
  FileTextIcon,
@@ -208,10 +210,10 @@ function Chalk(props) {
208
210
  props.onColorSchemeChange?.(next);
209
211
  };
210
212
  const [reportOpen, setReportOpen] = useState2(false);
211
- const reportTrigger = useRef(null);
212
- const openReport = props.onReportProblem ? () => {
213
- reportTrigger.current = document.activeElement instanceof HTMLButtonElement ? document.activeElement : null;
214
- setReportOpen(true);
213
+ const container = useRef(null);
214
+ const openReport = props.onReportProblem && !props.reportUnavailableReason ? () => {
215
+ if (props.onOpenReportProblem) props.onOpenReportProblem();
216
+ else setReportOpen(true);
215
217
  } : void 0;
216
218
  const suppliedClient = props.client;
217
219
  const getAccessRef = useRef(props.getAccess);
@@ -233,12 +235,23 @@ function Chalk(props) {
233
235
  void ownedClient.leave().catch(() => void 0).finally(() => ownedClient.dispose());
234
236
  };
235
237
  }, [ownedClient]);
236
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("div", { "data-chalk": true, "data-chalk-theme": colorScheme, className: "chalk-root h-full min-h-0 w-full", style: chalkThemeStyle(props.theme, colorScheme), children: /* @__PURE__ */ jsxs(ChalkProvider, { client, children: [
238
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("div", { ref: container, "data-chalk": true, "data-chalk-theme": colorScheme, className: "chalk-root h-full min-h-0 w-full", style: chalkThemeStyle(props.theme, colorScheme), children: /* @__PURE__ */ jsxs(ChalkProvider, { client, children: [
237
239
  /* @__PURE__ */ jsx(SpaceExperience, { ...props, theme: { ...props.theme, colorScheme }, onColorSchemeChange: changeColorScheme, resolvedColorScheme: colorScheme, onOpenReport: openReport }),
238
- props.onReportProblem ? /* @__PURE__ */ jsxs(Fragment, { children: [
240
+ props.onReportProblem && !props.onOpenReportProblem ? /* @__PURE__ */ jsxs(Fragment, { children: [
239
241
  /* @__PURE__ */ jsx(ShellStyles, {}),
240
- /* @__PURE__ */ jsx(CallDialog, { kind: "report", open: reportOpen, onClose: () => setReportOpen(false), onReportProblem: props.onReportProblem, diagnostics: `Call: ${props.spaceName ?? props.space ?? "Call"}
241
- Status: ${client.getSnapshot().connection.status}`, returnFocus: reportTrigger })
242
+ /* @__PURE__ */ jsx(
243
+ CallDialog,
244
+ {
245
+ kind: "report",
246
+ open: reportOpen,
247
+ onClose: () => setReportOpen(false),
248
+ onReportProblem: props.onReportProblem,
249
+ unavailableReason: props.reportUnavailableReason,
250
+ diagnostics: `Call: ${props.spaceName ?? props.space ?? "Call"}
251
+ Status: ${client.getSnapshot().connection.status}`,
252
+ focusScope: container
253
+ }
254
+ )
242
255
  ] }) : null
243
256
  ] }) }) });
244
257
  }
@@ -312,10 +325,21 @@ function SpaceExperience(props) {
312
325
  if (connection.status === "joining") return /* @__PURE__ */ jsx(CallLimbo, { theme: { ...props.theme, colorScheme: props.resolvedColorScheme }, recorded, spaceName, message: `Joining ${spaceName}\u2026` });
313
326
  if (connection.status === "failed" && connection.lastError?.code === "episode.ended") return /* @__PURE__ */ jsx(StatusSurface, { phase: "episode-ended", callName, theme: statusTheme, onHome: props.onHome, onReportProblem: props.onOpenReport });
314
327
  if (connection.status === "failed") {
315
- return /* @__PURE__ */ jsx(StatusSurface, { phase: "failed", callName, errorCode: connection.lastError?.code, theme: statusTheme, onHome: props.onHome, onReportProblem: props.onOpenReport, onRetry: retryAutomaticJoin, retryPending });
328
+ return /* @__PURE__ */ jsx(StatusSurface, { phase: "failed", callName, errorCode: connection.lastError?.code, sync: connection.lastError?.sync, theme: statusTheme, onHome: props.onHome, onReportProblem: props.onOpenReport, onRetry: retryAutomaticJoin, retryPending });
316
329
  }
317
330
  if (connection.status === "leaving" || connection.status === "left")
318
- return /* @__PURE__ */ jsx(StatusSurface, { phase: episodeEnded ? "episode-ended" : "left", callName, theme: statusTheme, onReportProblem: props.onOpenReport, onRejoin: props.onRejoin ?? retryAutomaticJoin, rejoinDisabled: connection.status === "leaving" || retryPending, onHome: props.onHome });
331
+ return /* @__PURE__ */ jsx(
332
+ StatusSurface,
333
+ {
334
+ phase: episodeEnded ? "episode-ended" : "left",
335
+ callName,
336
+ theme: statusTheme,
337
+ onReportProblem: connection.status === "leaving" ? void 0 : props.onOpenReport,
338
+ onRejoin: props.onRejoin ?? retryAutomaticJoin,
339
+ rejoinDisabled: connection.status === "leaving" || retryPending,
340
+ onHome: props.onHome
341
+ }
342
+ );
319
343
  return /* @__PURE__ */ jsxs("div", { className: "relative h-full min-h-0", children: [
320
344
  /* @__PURE__ */ jsx(SpaceSurface, { ...props, spaceName }),
321
345
  /* @__PURE__ */ jsx(CallLimbo, { overlay: true, active: false, initiallyCovered: true, message: "" })
@@ -327,14 +351,20 @@ function SpaceSurface(props) {
327
351
  const media = useMedia();
328
352
  const whiteboardState = useWhiteboard();
329
353
  const canEndEpisode = useCan("endEpisode");
330
- const canDrawWhiteboard = useCan("drawWhiteboard");
331
- const [commandError, setCommandError] = useState2(() => connection.lastError?.recoverable && connection.lastError.code.startsWith("media.") ? connection.lastError.message : null);
332
- const runCommand = useCallback(async (command) => {
354
+ const canDrawWhiteboard = useCanDrawWhiteboard();
355
+ const [commandError, setCommandError] = useState2(() => connection.lastError?.recoverable && connection.lastError.code.startsWith("media.") ? connection.lastError : null);
356
+ const [commandSource, setCommandSource] = useState2();
357
+ const [commandAction, setCommandAction] = useState2();
358
+ const runCommand = useCallback(async (command, source, action) => {
333
359
  try {
334
360
  await command();
335
361
  setCommandError(null);
362
+ setCommandSource(void 0);
363
+ setCommandAction(void 0);
336
364
  } catch (cause) {
337
- setCommandError(cause instanceof Error ? cause.message : "This command could not be completed.");
365
+ setCommandError(commandFailure(cause));
366
+ setCommandSource(source);
367
+ setCommandAction(action);
338
368
  }
339
369
  }, []);
340
370
  const whiteboardTransport = client.whiteboard.transport();
@@ -371,11 +401,15 @@ function SpaceSurface(props) {
371
401
  colorScheme: props.resolvedColorScheme,
372
402
  shareUrl: props.shareUrl,
373
403
  onReportProblem: props.onReportProblem,
404
+ onOpenReportProblem: props.onOpenReport,
405
+ reportUnavailableReason: props.reportUnavailableReason,
374
406
  onColorSchemeChange: props.onColorSchemeChange,
375
407
  spaceName: props.spaceName,
376
408
  inviteLink: props.inviteLink,
377
409
  pickChatFiles: props.pickChatFiles,
378
410
  commandError: commandError ?? void 0,
411
+ commandSource,
412
+ commandAction,
379
413
  onDismissCommandError: () => setCommandError(null),
380
414
  layout: props.layout,
381
415
  onLayoutChange: props.onLayoutChange,
@@ -385,7 +419,14 @@ function SpaceSurface(props) {
385
419
  onLeft: () => runCommand(() => client.leave()),
386
420
  onEndEpisode: canEndEpisode ? () => runCommand(() => client.endEpisode()) : void 0,
387
421
  reconnecting: { isVisible: connection.status === "reconnecting", status: "reconnecting", showNotice: false },
388
- overlay: /* @__PURE__ */ jsx(Fragment, { children: media.incomingRequests[0] ? /* @__PURE__ */ jsx(MediaRequestDialog, { request: media.incomingRequests[0], onDecline: () => void runCommand(() => client.media.declineRequest(media.incomingRequests[0].requestId)), onAllow: () => void runCommand(() => client.media.acceptRequest(media.incomingRequests[0].requestId)) }) : null })
422
+ overlay: /* @__PURE__ */ jsx(Fragment, { children: media.incomingRequests[0] ? /* @__PURE__ */ jsx(
423
+ MediaRequestDialog,
424
+ {
425
+ request: media.incomingRequests[0],
426
+ onDecline: () => void runCommand(() => client.media.declineRequest(media.incomingRequests[0].requestId), media.incomingRequests[0].kind === "start_camera" ? "camera" : "microphone", "request"),
427
+ onAllow: () => void runCommand(() => client.media.acceptRequest(media.incomingRequests[0].requestId), media.incomingRequests[0].kind === "start_camera" ? "camera" : "microphone", "request")
428
+ }
429
+ ) : null })
389
430
  }
390
431
  );
391
432
  }
@@ -891,19 +932,13 @@ function RecordingWhiteboardView({ state: stateValue, files, expectedSceneId, ex
891
932
  RecordingWhiteboardView.displayName = "RecordingWhiteboardView";
892
933
 
893
934
  // src/components/logo/Logo.tsx
894
- import { useId } from "react";
895
935
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
896
- var DEFAULT_COLOR = "#1A332B";
936
+ var DEFAULT_COLOR = "currentColor";
897
937
  var DEFAULT_HEIGHT = 32;
898
938
  var DEFAULT_VARIANT = "wordmark";
899
939
  var WORDMARK_VIEW_BOX = "0 0 200 80";
900
940
  var MARK_VIEW_BOX = "0 0 68 80";
901
- function gradientId(prefix, name) {
902
- return `${prefix}-${name}`;
903
- }
904
941
  function Logo({ accessibilityLabel = "Chalk", color = DEFAULT_COLOR, height = DEFAULT_HEIGHT, variant = DEFAULT_VARIANT }) {
905
- const generatedId = useId().replace(/[^a-zA-Z0-9_-]/gu, "");
906
- const idPrefix = `chalk-logo-${generatedId}`;
907
942
  const isWordmark = variant === "wordmark";
908
943
  const viewBox = isWordmark ? WORDMARK_VIEW_BOX : MARK_VIEW_BOX;
909
944
  const width = isWordmark ? height * 2.5 : height * (68 / 80);
@@ -924,41 +959,14 @@ function Logo({ accessibilityLabel = "Chalk", color = DEFAULT_COLOR, height = DE
924
959
  xmlns: "http://www.w3.org/2000/svg",
925
960
  fill: "none",
926
961
  children: [
927
- /* @__PURE__ */ jsxs3("defs", { children: [
928
- /* @__PURE__ */ jsxs3("linearGradient", { id: gradientId(idPrefix, "green"), x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: [
929
- /* @__PURE__ */ jsx5("stop", { offset: "0%", stopColor: "#C5E5C0" }),
930
- /* @__PURE__ */ jsx5("stop", { offset: "100%", stopColor: "#80B879" })
931
- ] }),
932
- /* @__PURE__ */ jsxs3("linearGradient", { id: gradientId(idPrefix, "yellow"), x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: [
933
- /* @__PURE__ */ jsx5("stop", { offset: "0%", stopColor: "#FCEAB3" }),
934
- /* @__PURE__ */ jsx5("stop", { offset: "100%", stopColor: "#D9B641" })
962
+ /* @__PURE__ */ jsxs3("svg", { x: "0", y: "6", width: "68", height: "68", viewBox: "0 0 48 48", children: [
963
+ /* @__PURE__ */ jsxs3("g", { transform: "rotate(28 24 24)", fill: "currentColor", children: [
964
+ /* @__PURE__ */ jsx5("path", { d: "M18 8h12v28a6 6 0 0 1-12 0z" }),
965
+ /* @__PURE__ */ jsx5("path", { d: "M18 8h12l-1.5 4.5h-9z", opacity: "0.45" })
935
966
  ] }),
936
- /* @__PURE__ */ jsxs3("linearGradient", { id: gradientId(idPrefix, "blue"), x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: [
937
- /* @__PURE__ */ jsx5("stop", { offset: "0%", stopColor: "#B2E0F0" }),
938
- /* @__PURE__ */ jsx5("stop", { offset: "100%", stopColor: "#55AAC9" })
939
- ] }),
940
- /* @__PURE__ */ jsxs3("linearGradient", { id: gradientId(idPrefix, "pink"), x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: [
941
- /* @__PURE__ */ jsx5("stop", { offset: "0%", stopColor: "#F8CACA" }),
942
- /* @__PURE__ */ jsx5("stop", { offset: "100%", stopColor: "#D67B7B" })
943
- ] })
944
- ] }),
945
- /* @__PURE__ */ jsxs3("g", { transform: "translate(2, 2) scale(0.95)", children: [
946
- /* @__PURE__ */ jsx5("g", { className: "chalk-logo__stick chalk-logo__stick--green", children: /* @__PURE__ */ jsxs3("g", { transform: "rotate(-15 20 60)", children: [
947
- /* @__PURE__ */ jsx5("path", { d: "M9,20 L21,20 L22,63 A7,7 0 0 1 8,63 Z", fill: `url(#${gradientId(idPrefix, "green")})` }),
948
- /* @__PURE__ */ jsx5("ellipse", { cx: "15", cy: "20", rx: "6", ry: "2.5", fill: "#D8ECD4" })
949
- ] }) }),
950
- /* @__PURE__ */ jsx5("g", { className: "chalk-logo__stick chalk-logo__stick--yellow", children: /* @__PURE__ */ jsxs3("g", { transform: "rotate(-8 32 55)", children: [
951
- /* @__PURE__ */ jsx5("path", { d: "M23,15 L35,15 L36,60 A7,7 0 0 1 22,60 Z", fill: `url(#${gradientId(idPrefix, "yellow")})` }),
952
- /* @__PURE__ */ jsx5("ellipse", { cx: "29", cy: "15", rx: "6", ry: "2.5", fill: "#FEF3D1" })
953
- ] }) }),
954
- /* @__PURE__ */ jsx5("g", { className: "chalk-logo__stick chalk-logo__stick--blue", children: /* @__PURE__ */ jsxs3("g", { transform: "rotate(12 60 30)", children: [
955
- /* @__PURE__ */ jsx5("path", { d: "M36,8 L48,8 L49,56 A7,7 0 0 1 35,56 Z", fill: `url(#${gradientId(idPrefix, "blue")})` }),
956
- /* @__PURE__ */ jsx5("ellipse", { cx: "42", cy: "8", rx: "6", ry: "2.5", fill: "#D0EDF8" })
957
- ] }) }),
958
- /* @__PURE__ */ jsx5("g", { className: "chalk-logo__stick chalk-logo__stick--pink", children: /* @__PURE__ */ jsxs3("g", { transform: "rotate(5 55 50)", children: [
959
- /* @__PURE__ */ jsx5("path", { d: "M49,70 L61,70 L62,29 A7,7 0 0 0 48,29 Z", fill: `url(#${gradientId(idPrefix, "pink")})` }),
960
- /* @__PURE__ */ jsx5("ellipse", { cx: "55", cy: "70", rx: "6", ry: "2.5", fill: "#FBE4E4" })
961
- ] }) })
967
+ /* @__PURE__ */ jsx5("circle", { cx: "10.5", cy: "41.5", r: "1.7", fill: "currentColor", opacity: "0.55" }),
968
+ /* @__PURE__ */ jsx5("circle", { cx: "6.5", cy: "37", r: "1.2", fill: "currentColor", opacity: "0.4" }),
969
+ /* @__PURE__ */ jsx5("circle", { cx: "4", cy: "43.5", r: "0.85", fill: "currentColor", opacity: "0.28" })
962
970
  ] }),
963
971
  isWordmark ? /* @__PURE__ */ jsx5("text", { className: "chalk-logo__wordmark", x: "90", y: "52", fontFamily: "system-ui, -apple-system, BlinkMacSystemFont, sans-serif", fontSize: "38", fontWeight: "600", letterSpacing: "-0.02em", fill: "currentColor", children: "chalk" }) : null
964
972
  ]