glitchgrab 1.57.0 → 1.58.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.
package/README.md CHANGED
@@ -1023,9 +1023,35 @@ if (!saved.ok) console.error(saved.error); // e.g. WhatsApp code already taken
1023
1023
  back instead of trusting what you sent.
1024
1024
  - **Never throws.** Failures come back as `{ ok: false, error }`, never as defaults.
1025
1025
 
1026
+ ### Asking questions before a demo is booked
1027
+
1028
+ Set `questions` and the booking dialog asks them before it shows the calendar;
1029
+ the WhatsApp booking chat asks the same ones. Each is a list of choices the
1030
+ prospect taps — which language they want the demo in, what kind of firm they
1031
+ run — so the right person on your team takes the call.
1032
+
1033
+ ```ts
1034
+ await updateBookingSettings({
1035
+ questions: [
1036
+ { id: "profession", label: "What best describes you?", options: ["CA", "CS", "Tax consultant", "Other"] },
1037
+ { id: "language", label: "Which language would you like the demo in?", options: ["English", "Hindi", "Marathi"] },
1038
+ ],
1039
+ });
1040
+
1041
+ const demos = await listBookings();
1042
+ demos[0].answers; // [{ id: "language", question: "Which language…", answer: "Hindi" }, …]
1043
+ ```
1044
+
1045
+ - Up to 5 questions, 2–9 choices each. `required: false` lets a prospect skip one.
1046
+ - Send the whole list to change it; `[]` asks nothing. Left out, it is kept.
1047
+ - Keep a question's `id` when you reword it, so earlier answers still line up.
1048
+ - The answers are also on the Google Calendar event and in the WhatsApp alert.
1049
+ - The dialog needs a `glitchgrab` version newer than 1.57 on the site. An older one
1050
+ still books, without asking.
1051
+
1026
1052
  ```ts
1027
1053
  getBookingSettings(options?: CallsOptions): Promise<BookingSettingsResult>
1028
- updateBookingSettings(changes: Partial<BookingSettings>, options?: CallsOptions): Promise<BookingSettingsResult>
1054
+ updateBookingSettings(changes: BookingSettingsChanges, options?: CallsOptions): Promise<BookingSettingsResult>
1029
1055
 
1030
1056
  type BookingSettingsResult =
1031
1057
  | { ok: true; data: { settings: BookingSettings; saved: boolean; calendarConnected: boolean } }
@@ -1044,6 +1070,15 @@ interface BookingSettings {
1044
1070
  horizonDays: number;
1045
1071
  noticeMinutes: number;
1046
1072
  whatsappCode: string | null;
1073
+ notifyRecipients: { name: string; phone: string }[]; // extra WhatsApp numbers alerted on a booking, max 5
1074
+ questions: BookingQuestion[]; // asked before the calendar, max 5
1075
+ }
1076
+
1077
+ interface BookingQuestion {
1078
+ id: string; // stable name, e.g. "language"; made from the label when left out
1079
+ label: string; // up to 80 characters
1080
+ options: string[]; // 2–9 choices, up to 40 characters each
1081
+ required: boolean; // false lets the prospect skip it
1047
1082
  }
1048
1083
  ```
1049
1084
 
package/dist/index.js CHANGED
@@ -97,6 +97,7 @@ function toAssistTurnResult(ok, body) {
97
97
  question: (_d = d.question) != null ? _d : null,
98
98
  options: Array.isArray(d.options) ? d.options.filter((o) => typeof o === "string") : [],
99
99
  report: (_e = d.report) != null ? _e : null,
100
+ title: typeof d.title === "string" && d.title.trim() ? d.title.trim() : null,
100
101
  duplicate: (_f = d.duplicate) != null ? _f : null,
101
102
  related: (_g = d.related) != null ? _g : null,
102
103
  solved: (_h = d.solved) != null ? _h : null,
@@ -2016,6 +2017,8 @@ function AssistSheet({
2016
2017
  reporterName,
2017
2018
  description,
2018
2019
  onDescriptionChange,
2020
+ issueTitle,
2021
+ onIssueTitleChange,
2019
2022
  severity,
2020
2023
  onSeverityChange,
2021
2024
  showSeverity,
@@ -2114,7 +2117,7 @@ function AssistSheet({
2114
2117
  onDegrade(message, retryable);
2115
2118
  }
2116
2119
  async function runTurn(history2, autoFile = false) {
2117
- var _a, _b;
2120
+ var _a, _b, _c;
2118
2121
  setBusy(true);
2119
2122
  setAutoFileLeft(null);
2120
2123
  let result;
@@ -2171,6 +2174,7 @@ function AssistSheet({
2171
2174
  }
2172
2175
  if (result.report) {
2173
2176
  onDescriptionChange(result.report);
2177
+ onIssueTitleChange((_b = result.title) != null ? _b : "");
2174
2178
  aiDraftRef.current = result.report;
2175
2179
  logEvent("draft_shown");
2176
2180
  setMessages([...history2, { role: "assistant", content: result.report, kind: "report" }]);
@@ -2183,7 +2187,7 @@ function AssistSheet({
2183
2187
  }
2184
2188
  if (result.question) {
2185
2189
  setMessages([...history2, { role: "assistant", content: result.question }]);
2186
- setOptions((_b = result.options) != null ? _b : []);
2190
+ setOptions((_c = result.options) != null ? _c : []);
2187
2191
  return;
2188
2192
  }
2189
2193
  onDegrade("The assistant had nothing to add \u2014 write your report below and send it as normal.");
@@ -3027,6 +3031,40 @@ function AssistSheet({
3027
3031
  title: true,
3028
3032
  footer: "Your details will be added to it instead of opening a new issue."
3029
3033
  }),
3034
+ !duplicate && issueTitle && // Wraps rather than scrolls sideways: a title cut off at the
3035
+ // box edge is the very thing #543 is about. One line of text,
3036
+ // so Enter is swallowed.
3037
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3038
+ "textarea",
3039
+ {
3040
+ "aria-label": "Title",
3041
+ value: issueTitle,
3042
+ maxLength: 120,
3043
+ rows: 2,
3044
+ onKeyDown: (e) => {
3045
+ if (e.key === "Enter") e.preventDefault();
3046
+ },
3047
+ onChange: (e) => {
3048
+ stopAutoFile("edited the title");
3049
+ onIssueTitleChange(e.target.value.replace(/[\r\n]+/g, " "));
3050
+ },
3051
+ style: {
3052
+ width: "100%",
3053
+ padding: "8px 10px",
3054
+ lineHeight: 1.45,
3055
+ resize: "none",
3056
+ borderRadius: "8px",
3057
+ border: `1px solid ${t.inputBorder}`,
3058
+ backgroundColor: t.inputBg,
3059
+ color: t.text,
3060
+ fontSize: "13px",
3061
+ fontWeight: 600,
3062
+ fontFamily: "inherit",
3063
+ outline: "none",
3064
+ boxSizing: "border-box"
3065
+ }
3066
+ }
3067
+ ),
3030
3068
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3031
3069
  "textarea",
3032
3070
  {
@@ -4668,6 +4706,7 @@ function ReportDialog({
4668
4706
  const [pickedSimilar, setPickedSimilar] = (0, import_react.useState)(null);
4669
4707
  const [addedToIssue, setAddedToIssue] = (0, import_react.useState)(null);
4670
4708
  const [aiConversationId, setAiConversationId] = (0, import_react.useState)(null);
4709
+ const [issueTitle, setIssueTitle] = (0, import_react.useState)("");
4671
4710
  const [isEnhanced, setIsEnhanced] = (0, import_react.useState)(false);
4672
4711
  const [originalDescription, setOriginalDescription] = (0, import_react.useState)(
4673
4712
  null
@@ -5369,6 +5408,7 @@ function ReportDialog({
5369
5408
  }
5370
5409
  if (aiConversationId) {
5371
5410
  metadata.aiConversationId = aiConversationId;
5411
+ if (sheetUp && !duplicateOf && issueTitle.trim()) metadata.issueTitle = issueTitle.trim();
5372
5412
  }
5373
5413
  const result = await report(
5374
5414
  // The RATING branch returned above, so anything reaching here is a real
@@ -5387,6 +5427,7 @@ function ReportDialog({
5387
5427
  setScreenshots([]);
5388
5428
  setAttachments([]);
5389
5429
  setAiConversationId(null);
5430
+ setIssueTitle("");
5390
5431
  setDuplicateIssueNumber(null);
5391
5432
  setDuplicateKind("duplicate");
5392
5433
  setSimilarIssues(null);
@@ -7490,6 +7531,8 @@ function ReportDialog({
7490
7531
  setDescription(value);
7491
7532
  if (validationError) setValidationError(null);
7492
7533
  },
7534
+ issueTitle,
7535
+ onIssueTitleChange: setIssueTitle,
7493
7536
  severity,
7494
7537
  onSeverityChange: (value) => {
7495
7538
  setSeverity(value);
@@ -7655,6 +7698,16 @@ function getTypeIcon(type, color, size = 24) {
7655
7698
  // src/booking-dialog.tsx
7656
7699
  var import_react5 = require("react");
7657
7700
  var import_jsx_runtime6 = require("react/jsx-runtime");
7701
+ function readQuestions(value) {
7702
+ if (!Array.isArray(value)) return [];
7703
+ return value.flatMap((row) => {
7704
+ if (!row || typeof row !== "object") return [];
7705
+ const { id, label, options, required } = row;
7706
+ if (typeof id !== "string" || typeof label !== "string" || !Array.isArray(options)) return [];
7707
+ const choices = options.filter((o) => typeof o === "string" && o !== "");
7708
+ return choices.length > 0 ? [{ id, label, options: choices, required: required !== false }] : [];
7709
+ });
7710
+ }
7658
7711
  var LIGHT = {
7659
7712
  bg: "#ffffff",
7660
7713
  pane: "#ffffff",
@@ -7808,6 +7861,10 @@ function BookingDialog({
7808
7861
  const [email, setEmail] = (0, import_react5.useState)("");
7809
7862
  const [phone, setPhone] = (0, import_react5.useState)("");
7810
7863
  const [note, setNote] = (0, import_react5.useState)("");
7864
+ const [questions, setQuestions] = (0, import_react5.useState)([]);
7865
+ const [answers, setAnswers] = (0, import_react5.useState)({});
7866
+ const [pageLoaded, setPageLoaded] = (0, import_react5.useState)(false);
7867
+ const [asked, setAsked] = (0, import_react5.useState)(false);
7811
7868
  const [code, setCode] = (0, import_react5.useState)("");
7812
7869
  const [bookingId, setBookingId] = (0, import_react5.useState)(null);
7813
7870
  const [meetUrl, setMeetUrl] = (0, import_react5.useState)(null);
@@ -7836,6 +7893,7 @@ function BookingDialog({
7836
7893
  if (!json.success) {
7837
7894
  setError((_a2 = json.error) != null ? _a2 : "Couldn't load available times");
7838
7895
  setSlotsByMonth((prev) => ({ ...prev, [target]: [] }));
7896
+ setPageLoaded(true);
7839
7897
  return;
7840
7898
  }
7841
7899
  const data = (_b2 = json.data) != null ? _b2 : {};
@@ -7848,9 +7906,12 @@ function BookingDialog({
7848
7906
  slotMinutes: typeof data.slotMinutes === "number" ? data.slotMinutes : 30
7849
7907
  });
7850
7908
  setWhatsappUrl((_c2 = data.whatsappUrl) != null ? _c2 : null);
7909
+ setQuestions(readQuestions(data.questions));
7910
+ setPageLoaded(true);
7851
7911
  } catch (e) {
7852
7912
  setError("Couldn't reach the booking service");
7853
7913
  setSlotsByMonth((prev) => ({ ...prev, [target]: [] }));
7914
+ setPageLoaded(true);
7854
7915
  }
7855
7916
  },
7856
7917
  [baseUrl, headers]
@@ -7868,6 +7929,8 @@ function BookingDialog({
7868
7929
  setBookingId(null);
7869
7930
  setError(null);
7870
7931
  setAvatarFailed(false);
7932
+ setPageLoaded(false);
7933
+ setAsked(false);
7871
7934
  }, [open]);
7872
7935
  const monthLoaded = month in slotsByMonth;
7873
7936
  (0, import_react5.useEffect)(() => {
@@ -7896,6 +7959,10 @@ function BookingDialog({
7896
7959
  const daySlots = day ? (_d = byDay.get(day)) != null ? _d : [] : [];
7897
7960
  const atFirstMonth = month <= today.slice(0, 7);
7898
7961
  const pastHorizon = monthLoaded && !error && byDay.size === 0 && !atFirstMonth;
7962
+ const given = questions.flatMap((q) => q.options.includes(answers[q.id]) ? [{ ...q, answer: answers[q.id] }] : []);
7963
+ const canContinue = questions.every((q) => !q.required || q.options.includes(answers[q.id]));
7964
+ const view = step !== "slots" ? step : !pageLoaded ? "loading" : questions.length > 0 && !asked ? "questions" : "slots";
7965
+ const roomy = step === "slots";
7899
7966
  async function startBooking() {
7900
7967
  var _a2;
7901
7968
  if (!selected) return;
@@ -7910,6 +7977,7 @@ function BookingDialog({
7910
7977
  email,
7911
7978
  phone,
7912
7979
  note,
7980
+ answers: Object.fromEntries(given.map((q) => [q.id, q.answer])),
7913
7981
  startsAt: selected.startsAt,
7914
7982
  // The zone the times were shown in, which is the one the visitor chose.
7915
7983
  timezone: zone
@@ -7960,7 +8028,7 @@ function BookingDialog({
7960
8028
  "div",
7961
8029
  {
7962
8030
  style: {
7963
- marginTop: wide ? whatsappUrl && step === "slots" ? 14 : "auto" : 0,
8031
+ marginTop: wide ? whatsappUrl && roomy ? 14 : "auto" : 0,
7964
8032
  padding: wide ? 0 : "4px 20px 18px",
7965
8033
  display: "flex",
7966
8034
  justifyContent: wide ? "flex-start" : "center"
@@ -8101,9 +8169,36 @@ function BookingDialog({
8101
8169
  selectedEnd && formatTime(selectedEnd, zone, twelve)
8102
8170
  ] })
8103
8171
  ] }) }),
8172
+ asked && given.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Row, { icon: "check", t, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
8173
+ given.map((q) => q.answer).join(" \xB7 "),
8174
+ view === "slots" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
8175
+ " ",
8176
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
8177
+ "button",
8178
+ {
8179
+ type: "button",
8180
+ onClick: () => setAsked(false),
8181
+ className: "gg-bk-link",
8182
+ style: {
8183
+ border: 0,
8184
+ // Room for a thumb without pushing the rows apart.
8185
+ padding: "8px 6px",
8186
+ margin: "-8px 0",
8187
+ background: "transparent",
8188
+ color: t.muted,
8189
+ fontFamily: "inherit",
8190
+ fontSize: 13,
8191
+ textDecoration: "underline",
8192
+ cursor: "pointer"
8193
+ },
8194
+ children: "Change"
8195
+ }
8196
+ )
8197
+ ] })
8198
+ ] }) }),
8104
8199
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Row, { icon: "clock", t, children: formatDuration(page.slotMinutes) }),
8105
8200
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Row, { icon: "video", t, children: "Google Meet" }),
8106
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Row, { icon: "globe", t, children: step === "slots" ? (
8201
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Row, { icon: "globe", t, children: roomy ? (
8107
8202
  // The visible label is plain text; the native select sits on top of
8108
8203
  // it, invisible. A styled select sizes itself to the longest zone
8109
8204
  // name in the list, which pushed the chevron to the pane's edge.
@@ -8123,7 +8218,7 @@ function BookingDialog({
8123
8218
  ] })
8124
8219
  ) : zone })
8125
8220
  ] }),
8126
- whatsappUrl && step === "slots" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
8221
+ whatsappUrl && roomy && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
8127
8222
  "a",
8128
8223
  {
8129
8224
  href: whatsappUrl,
@@ -8394,6 +8489,93 @@ function BookingDialog({
8394
8489
  ]
8395
8490
  }
8396
8491
  );
8492
+ const loading = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
8493
+ "div",
8494
+ {
8495
+ style: {
8496
+ flex: "1 1 auto",
8497
+ minHeight: wide ? void 0 : 200,
8498
+ display: "flex",
8499
+ alignItems: "center",
8500
+ justifyContent: "center",
8501
+ fontSize: 13,
8502
+ ...muted
8503
+ },
8504
+ children: "Loading\u2026"
8505
+ }
8506
+ );
8507
+ const ask = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
8508
+ "div",
8509
+ {
8510
+ style: {
8511
+ flex: "1 1 auto",
8512
+ minWidth: 0,
8513
+ padding: wide ? "28px 32px" : "20px",
8514
+ boxSizing: "border-box",
8515
+ overflowY: wide ? "auto" : void 0
8516
+ },
8517
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { maxWidth: 520, display: "flex", flexDirection: "column", gap: 22 }, children: [
8518
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
8519
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 16, fontWeight: 600 }, children: questions.length === 1 ? "One question first" : "A few questions first" }),
8520
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 13, lineHeight: 1.5, marginTop: 4, ...muted }, children: "So the right person takes your demo." })
8521
+ ] }),
8522
+ questions.map((q) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { role: "group", "aria-label": q.label, children: [
8523
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { fontSize: 14, fontWeight: 500, overflowWrap: "anywhere" }, children: [
8524
+ q.label,
8525
+ !q.required && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontWeight: 400, ...muted }, children: " (optional)" })
8526
+ ] }),
8527
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: 8, marginTop: 10 }, children: q.options.map((option) => {
8528
+ const on = answers[q.id] === option;
8529
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
8530
+ "button",
8531
+ {
8532
+ type: "button",
8533
+ "aria-pressed": on,
8534
+ className: on ? void 0 : "gg-bk-slot",
8535
+ onClick: () => setAnswers((prev) => {
8536
+ if (on && !q.required) {
8537
+ const rest = { ...prev };
8538
+ delete rest[q.id];
8539
+ return rest;
8540
+ }
8541
+ return { ...prev, [q.id]: option };
8542
+ }),
8543
+ style: {
8544
+ padding: "9px 14px",
8545
+ minHeight: 40,
8546
+ borderRadius: 8,
8547
+ border: `1px solid ${on ? t.ink : t.border}`,
8548
+ background: on ? t.ink : t.pane,
8549
+ color: on ? t.onInk : t.text,
8550
+ fontFamily: "inherit",
8551
+ fontSize: 14,
8552
+ fontWeight: 500,
8553
+ cursor: "pointer",
8554
+ overflowWrap: "anywhere",
8555
+ textAlign: "left"
8556
+ },
8557
+ children: option
8558
+ },
8559
+ option
8560
+ );
8561
+ }) })
8562
+ ] }, q.id)),
8563
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", alignItems: "center", gap: "8px 12px", marginTop: 2 }, children: [
8564
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
8565
+ "button",
8566
+ {
8567
+ type: "button",
8568
+ onClick: () => setAsked(true),
8569
+ disabled: !canContinue,
8570
+ style: { ...primaryStyle(t, !canContinue), whiteSpace: "nowrap" },
8571
+ children: "See available times"
8572
+ }
8573
+ ),
8574
+ !canContinue && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontSize: 12, ...muted }, children: "Pick one for each to continue" })
8575
+ ] })
8576
+ ] })
8577
+ }
8578
+ );
8397
8579
  const form = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
8398
8580
  "div",
8399
8581
  {
@@ -8507,6 +8689,7 @@ function BookingDialog({
8507
8689
  .gg-bk-day:hover { box-shadow: inset 0 0 0 1.5px ${t.ink}; }
8508
8690
  .gg-bk-slot:hover { border-color: ${t.ink} !important; }
8509
8691
  .gg-bk-ghost:hover { background: ${t.open} !important; }
8692
+ .gg-bk-link:hover { color: ${t.text} !important; }
8510
8693
  .gg-bk-root button:focus-visible, .gg-bk-root a:focus-visible,
8511
8694
  .gg-bk-root span:has(> select:focus-visible), .gg-bk-root input:focus-visible, .gg-bk-root textarea:focus-visible {
8512
8695
  outline: 2px solid ${t.ink}; outline-offset: 2px;
@@ -8520,11 +8703,11 @@ function BookingDialog({
8520
8703
  "aria-label": page.title,
8521
8704
  className: "gg-bk-root",
8522
8705
  style: {
8523
- width: step === "slots" ? "min(1060px, 100%)" : "min(820px, 100%)",
8706
+ width: roomy ? "min(1060px, 100%)" : "min(820px, 100%)",
8524
8707
  // A month grid should not jump in height between months; the form
8525
8708
  // steps are short and would sit in a mostly empty box.
8526
- height: wide && step === "slots" ? "min(640px, calc(100vh - 48px))" : void 0,
8527
- minHeight: wide && step !== "slots" ? 420 : void 0,
8709
+ height: wide && roomy ? "min(640px, calc(100vh - 48px))" : void 0,
8710
+ minHeight: wide && !roomy ? 420 : void 0,
8528
8711
  margin: wide ? void 0 : "auto 0",
8529
8712
  display: "flex",
8530
8713
  flexDirection: wide ? "row" : "column",
@@ -8540,7 +8723,7 @@ function BookingDialog({
8540
8723
  },
8541
8724
  children: [
8542
8725
  summary,
8543
- step === "slots" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
8726
+ view === "loading" ? loading : view === "questions" ? ask : view === "slots" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
8544
8727
  calendar,
8545
8728
  times
8546
8729
  ] }) : form,
@@ -8627,6 +8810,7 @@ function Icon({ name }) {
8627
8810
  left: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m15 18-6-6 6-6" }),
8628
8811
  right: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m9 18 6-6-6-6" }),
8629
8812
  down: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m6 9 6 6 6-6" }),
8813
+ check: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M20 6 9 17l-5-5" }),
8630
8814
  clock: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
8631
8815
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "12", cy: "12", r: "9" }),
8632
8816
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M12 7v5l3 2" })