@wix/himalaya-cli 0.970.0 → 0.971.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 (48) hide show
  1. package/dist/cli.mjs +196 -160
  2. package/dist/preview-assets.generated.bin +0 -0
  3. package/guides/tier-5-add-a-screen.md +8 -4
  4. package/package.json +2 -2
  5. package/templates/beauty-salon-owner/config.ts +1 -1
  6. package/templates/beauty-salon-owner/dev/index.ts +79 -1
  7. package/templates/beauty-salon-owner/tier5-src/_shared/aria-chat.ts +176 -6
  8. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-aria/index.ts +15 -0
  9. package/templates/community-club-owner/config.ts +1 -1
  10. package/templates/community-club-owner/dev/index.ts +79 -1
  11. package/templates/community-club-owner/tier5-src/_shared/aria-chat.ts +176 -6
  12. package/templates/community-club-owner/tier5-src/himi-owner-lite-aria/index.ts +15 -0
  13. package/templates/events-meetups-owner/config.ts +1 -1
  14. package/templates/events-meetups-owner/dev/index.ts +79 -1
  15. package/templates/events-meetups-owner/tier5-src/_shared/aria-chat.ts +176 -6
  16. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-aria/index.ts +15 -0
  17. package/templates/gym-fitness-owner/config.ts +1 -1
  18. package/templates/gym-fitness-owner/dev/index.ts +79 -1
  19. package/templates/gym-fitness-owner/tier5-src/_shared/aria-chat.ts +176 -6
  20. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-aria/index.ts +15 -0
  21. package/templates/nonprofit-cause-owner/config.ts +1 -1
  22. package/templates/nonprofit-cause-owner/dev/index.ts +79 -1
  23. package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-chat.ts +176 -6
  24. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-aria/index.ts +15 -0
  25. package/templates/owner-lite/config.ts +1 -1
  26. package/templates/owner-lite/dev/index.ts +79 -1
  27. package/templates/owner-lite/tier5-src/_shared/aria-chat.ts +176 -6
  28. package/templates/owner-lite/tier5-src/himi-owner-lite-aria/index.ts +15 -0
  29. package/templates/podcast-creator-owner/config.ts +1 -1
  30. package/templates/podcast-creator-owner/dev/index.ts +79 -1
  31. package/templates/podcast-creator-owner/tier5-src/_shared/aria-chat.ts +176 -6
  32. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-aria/index.ts +15 -0
  33. package/templates/portfolio-creative-owner/config.ts +1 -1
  34. package/templates/portfolio-creative-owner/dev/index.ts +79 -1
  35. package/templates/portfolio-creative-owner/tier5-src/_shared/aria-chat.ts +176 -6
  36. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-aria/index.ts +15 -0
  37. package/templates/restaurant-owner/config.ts +1 -1
  38. package/templates/restaurant-owner/dev/index.ts +79 -1
  39. package/templates/restaurant-owner/tier5-src/_shared/aria-chat.ts +176 -6
  40. package/templates/restaurant-owner/tier5-src/himi-owner-lite-aria/index.ts +15 -0
  41. package/templates/retail-store-owner/config.ts +1 -1
  42. package/templates/retail-store-owner/dev/index.ts +79 -1
  43. package/templates/retail-store-owner/tier5-src/_shared/aria-chat.ts +176 -6
  44. package/templates/retail-store-owner/tier5-src/himi-owner-lite-aria/index.ts +15 -0
  45. package/templates/yoga-studio-owner/config.ts +1 -1
  46. package/templates/yoga-studio-owner/dev/index.ts +79 -1
  47. package/templates/yoga-studio-owner/tier5-src/_shared/aria-chat.ts +176 -6
  48. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-aria/index.ts +15 -0
@@ -89,6 +89,19 @@ export interface AriaChatState {
89
89
  error: string;
90
90
  /** Bound seed for the composer field. */
91
91
  input: string;
92
+ /** Transient result of the last Copy: the confirmation, or the failure.
93
+ *
94
+ * It exists because `writeText` resolves or throws and NOTHING else in the
95
+ * state changes either way — so without this the owner cannot tell a copy
96
+ * that took from one the platform refused. Saying "Copied" when nothing
97
+ * reached the clipboard is a known way for a copy control to lie; this is
98
+ * the field that makes the difference observable. */
99
+ copyNote: string;
100
+ /** Collapses the Copy / Regenerate row. Derived, like `stopHidden`: shown
101
+ * only when the newest bubble is a REPLY (not the owner's message, not the
102
+ * local greeting) and no turn is out. A pure function of other fields, so it
103
+ * is computed once in `gated` rather than maintained by every reducer. */
104
+ replyActionsHidden: boolean;
92
105
  messages: AriaBubble[];
93
106
  /** Non-empty ONLY on the greeting-only empty state. */
94
107
  prompts: AriaPromptChip[];
@@ -199,6 +212,12 @@ export interface AriaChatLabels {
199
212
  /** Status line after the owner stopped a reply. Sits in the NEUTRAL slot:
200
213
  * they asked for it, so nothing went wrong. */
201
214
  stopped?: Resolvable<string>;
215
+ /** Shown after a clipboard write that RESOLVED. */
216
+ copied?: Resolvable<string>;
217
+ /** Shown after a clipboard write that threw, or when the host supplied no
218
+ * clipboard at all. Deliberately distinct from `copied`: the point of the
219
+ * pair is that the owner can tell which of the two happened. */
220
+ copyFailed?: Resolvable<string>;
202
221
  /** Shown under a picker once the owner has chosen as many options as the
203
222
  * service allows. Takes the limit, because "pick up to 3" is the whole of
204
223
  * the information. */
@@ -242,6 +261,13 @@ export interface AriaChatOptions {
242
261
  /** Opens an external URL. Apps whose descriptors carry their own open-url
243
262
  * action (the clone) leave this unset and never invoke `openLink`. */
244
263
  openLink?: (url: string) => Promise<boolean>;
264
+
265
+ /** Put `text` on the system clipboard. Injected rather than imported so the
266
+ * shared core stays free of the SDK, exactly as `openLink` is.
267
+ *
268
+ * It RESOLVES or THROWS — there is no boolean. A host that does not supply
269
+ * it has no clipboard, and `copyMessage` says so rather than pretending. */
270
+ copyText?: (text: string) => Promise<void>;
245
271
  /** Maps an Aria action-link URL to an in-app screen. A hit navigates via a
246
272
  * `_nav` directive — the host applies the same linkable-target check an
247
273
  * authored `himi.open` gets — and a miss falls through to `openLink`. */
@@ -573,6 +599,16 @@ export function toBubbles(msgs: ConversationMessage[], assistantName: string, yo
573
599
  .filter((b) => b.content && b.content.trim());
574
600
  }
575
601
 
602
+ /** The reply the Copy / Regenerate row acts on: the LAST bubble, when it is an
603
+ * answer. Not "the newest assistant bubble": a widget-only answer leaves no
604
+ * bubble, so the transcript then ends on the owner's question, and searching
605
+ * back past it would offer the previous turn's answer as if it were this one.
606
+ * The greeting is excluded because it is a local bubble, not a reply. */
607
+ function lastReply(s: { messages: AriaBubble[] }): AriaBubble | undefined {
608
+ const last = s.messages[s.messages.length - 1];
609
+ return last && last.notUser && last.id !== GREETING_ID ? last : undefined;
610
+ }
611
+
576
612
  /** Chips are visible only before the owner has said anything. */
577
613
  function promptsFor(msgs: AriaBubble[], prompts: AriaPromptChip[]): AriaPromptChip[] {
578
614
  return msgs.some((b) => b.role === "USER") ? [] : prompts;
@@ -845,6 +881,24 @@ export interface AriaChatCore {
845
881
  * that moved during the abort makes this snapshot stale, and committing it
846
882
  * would end a turn the owner has since started. */
847
883
  stop(s: AriaChatState): Promise<AriaChatState | undefined>;
884
+
885
+ /** Put one reply's prose on the clipboard. Defaults to the newest assistant
886
+ * bubble, which is the only one the descriptor offers the control on.
887
+ *
888
+ * Copies `bubble.content`, which is ALREADY the normalized prose — the
889
+ * envelope scan and the markdown stripper both ran on the way in
890
+ * (`bubblesFrom`), so the owner gets what they can read rather than the
891
+ * wire text. */
892
+ /** `undefined` when the turn moved on while the write was out — see `stop`. */
893
+ copyMessage(s: AriaChatState, arg?: AriaActionArg): Promise<AriaChatState | undefined>;
894
+
895
+ /** Ask for another answer.
896
+ *
897
+ * NOT a protocol feature: it sends ordinary user text, the same literal the
898
+ * native Aria app sends, through the one send path (`send`). Not through
899
+ * `submit`: with no argument that reads the COMPOSER, so a regenerate would
900
+ * send whatever the owner had half-typed instead of the retry. */
901
+ regenerate(s: AriaChatState): Promise<AriaChatState>;
848
902
  /** Tapping the active widget's primary / secondary footer button.
849
903
  *
850
904
  * The url is read from STATE rather than bound into the action's params.
@@ -880,6 +934,10 @@ export interface AriaChatCore {
880
934
  }
881
935
 
882
936
  const POLL_ID = "aria-turn";
937
+ /** What Regenerate sends. Ordinary user text, matching the native Aria app —
938
+ * the chat-service has no retry verb, and inventing one here would be a
939
+ * protocol we then had to keep. */
940
+ const REGENERATE_TEXT = "Let's try that again.";
883
941
  /** Id of the local greeting bubble. Shared by the minting and the
884
942
  * "is it still the empty state?" test so the two cannot drift apart. */
885
943
  const GREETING_ID = "greeting";
@@ -915,7 +973,7 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
915
973
  const localHistory = o.localHistory !== false;
916
974
 
917
975
  const initialState: AriaChatState = {
918
- status: "loading", statusText: "", error: "", input: "",
976
+ status: "loading", statusText: "", error: "", input: "", copyNote: "", replyActionsHidden: true,
919
977
  messages: [], prompts: [], _convId: "", _siteId: "", _pending: "",
920
978
  _pendingAfter: 0, _replyAfter: 0, _srvUsers: 0, _srvAssistants: 0,
921
979
  _abortId: "", _turnId: "", stopHidden: true, ok: false,
@@ -966,6 +1024,10 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
966
1024
  * those. `submitting` stays on the state because the descriptor needs
967
1025
  * something to disable a button with; it is the shadow, not the guard. */
968
1026
  let widgetSending = false;
1027
+ /** A regenerate is on its way. Refuses a double-tap in the window before
1028
+ * `send` has written `_pending` — the same window `widgetSending` next door
1029
+ * closes for two taps of one option. */
1030
+ let regenerating = false;
969
1031
 
970
1032
  /** A turn the server STORED but never got asked to answer.
971
1033
  *
@@ -1188,7 +1250,10 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
1188
1250
  // state this action RETURNS, so on the `_poll` path they paint immediately.
1189
1251
  const optimistic: AriaBubble[] = [...s.messages, plainBubble(`u${s.messages.length}`, "USER", you(), content)];
1190
1252
  const base: AriaChatState = {
1191
- ...s, prompts: [], input: "", error: "",
1253
+ // `copyNote: ""` — a Copy confirmation belongs to the moment, and a new
1254
+ // turn ends it. Left in place it would sit under the next reply saying
1255
+ // "Copied" about text the owner copied several turns ago.
1256
+ ...s, prompts: [], input: "", error: "", copyNote: "",
1192
1257
  // The PREVIOUS turn's handles must not survive into this one: aborting by
1193
1258
  // a stale id would abandon a reply that has already been read, and waiting
1194
1259
  // on a stale turn id would look for an answer that already exists. Both
@@ -1410,9 +1475,36 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
1410
1475
  * function of two other fields, and eight reducers each remembering to set it
1411
1476
  * is eight chances for the control to be shown when there is nothing to stop
1412
1477
  * — or, worse, hidden while a reply the owner wants to abandon runs on. */
1478
+ let committed: AriaChatState | null = null;
1479
+ let commits = 0;
1480
+ /** A result as the runtime stores it: the one-shot directives it strips on
1481
+ * commit (`_stream`, `_nav`, `_poll`, `_error` — see `applyNext` in the web
1482
+ * host's runtime bootstrap) removed. */
1483
+ const settled = (s: AriaChatState): AriaChatState => {
1484
+ const r = s as AriaChatState & Record<string, unknown>;
1485
+ if (r._stream === undefined && r._nav === undefined && r._poll === undefined && r._error === undefined) return s;
1486
+ const { _stream: _a, _nav: _b, _poll: _c, _error: _d, ...rest } = r;
1487
+ return rest as AriaChatState;
1488
+ };
1413
1489
  const gated = (s: AriaChatState): AriaChatState => {
1414
1490
  const hidden = !(turnInFlight(s) && !!s._abortId);
1415
- return s.stopHidden === hidden ? s : { ...s, stopHidden: hidden };
1491
+ // The reply actions follow the same rule for the same reason: a derived
1492
+ // flag, recomputed on every action's way out, so no reducer can forget it.
1493
+ // `_pending` rather than `turnInFlight` — see `regenerate` for why `status`
1494
+ // is not trusted to have been cleared.
1495
+ const replyActionsHidden = !(lastReply(s) && !s._pending);
1496
+ const out = s.stopHidden === hidden && s.replyActionsHidden === replyActionsHidden
1497
+ ? s
1498
+ : { ...s, stopHidden: hidden, replyActionsHidden };
1499
+ // Every state the runtime commits passes through here (an `undefined`
1500
+ // result commits nothing and skips it), so this is the newest committed
1501
+ // state — what `copyMessage` puts its note on when other actions landed
1502
+ // while its write was out. Kept as the runtime STORES it, i.e. without the
1503
+ // transient directives it strips on commit: re-returning a `_nav` or a
1504
+ // `_poll` from here would navigate, or re-arm the timer, a second time.
1505
+ committed = settled(out);
1506
+ commits += 1;
1507
+ return out;
1416
1508
  };
1417
1509
 
1418
1510
  /** Ask the service how an async job is getting on.
@@ -1550,7 +1642,10 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
1550
1642
  const raw = messagesOf(gm);
1551
1643
  const bubbles = toBubbles(raw, assistant(), you());
1552
1644
  const resumed = bubbles.length ? bubbles : greetingOnly();
1553
- return { ...s, status: "ready", _convId: resumeId, _siteId: site, _pending: "", statusText: "", error: "",
1645
+ // `copyNote: ""` — a resumed transcript ends in a reply, so the row is
1646
+ // shown at once; a note carried over would say "Copied" beside an
1647
+ // answer from a different conversation that nobody copied.
1648
+ return { ...s, status: "ready", _convId: resumeId, _siteId: site, _pending: "", statusText: "", error: "", copyNote: "",
1554
1649
  messages: resumed, ...observed(raw, s.widget), prompts: promptsFor(resumed, promptList()) };
1555
1650
  } catch { /* expired or unreachable — fall through to a fresh one */ }
1556
1651
  }
@@ -1649,11 +1744,11 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
1649
1744
  // previous one, which reads as ➕ having done nothing.
1650
1745
  turnSeq += 1;
1651
1746
  return slow({ ...s, status: "ready", _convId: conversationIdOf(res), _siteId: site, input: "", statusText: "", error: "",
1652
- _pending: "", _abortId: "", _turnId: "", messages: greetingOnly(), ...observed([]), prompts: promptList(), ok: false });
1747
+ _pending: "", _abortId: "", _turnId: "", copyNote: "", messages: greetingOnly(), ...observed([]), prompts: promptList(), ok: false });
1653
1748
  } catch (e) {
1654
1749
  turnSeq += 1; // see above — a failed create commits state too
1655
1750
  return slow({ ...s, status: "ready", _convId: "", _siteId: site, input: "", ...failureSlots(e, unavailableText),
1656
- _pending: "", _abortId: "", _turnId: "", messages: greetingOnly(), ...observed([]), prompts: promptList(), ok: false });
1751
+ _pending: "", _abortId: "", _turnId: "", copyNote: "", messages: greetingOnly(), ...observed([]), prompts: promptList(), ok: false });
1657
1752
  }
1658
1753
  },
1659
1754
 
@@ -1972,6 +2067,79 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
1972
2067
  return slow(withDraft(
1973
2068
  { ...s, status: "ready", statusText: stoppedText(), error: "", _pending: "", _abortId: "" }, typedAt));
1974
2069
  },
2070
+
2071
+ copyMessage: async (s, arg) => {
2072
+ // Which bubble. The descriptor offers Copy on the newest assistant reply,
2073
+ // so the id is optional and that bubble is the default — but an id IS
2074
+ // accepted, because a row-scoped control passing `$item.id` is the
2075
+ // established shape on this screen (the link rows do exactly that).
2076
+ const wanted = typeof arg === "object" && arg !== null ? String((arg as { id?: string }).id ?? "") : "";
2077
+ const target = wanted
2078
+ ? s.messages.find((m) => m.id === wanted)
2079
+ // The LAST bubble, and only if it is a reply — the same test `gated`
2080
+ // shows the row on. Searching back for the newest reply instead would,
2081
+ // after a widget-only answer (which leaves no bubble), skip over the
2082
+ // owner's question and copy the answer to the turn BEFORE it. The
2083
+ // greeting is excluded because it is a local bubble, not a reply.
2084
+ : lastReply(s);
2085
+ // `content` is ALREADY the prose: `bubblesFrom` scans the widget envelope
2086
+ // out first and then strips markdown, so this is what the owner can read
2087
+ // rather than the wire text.
2088
+ const text = (target?.content ?? "").trim();
2089
+ const failed = () => res(o.labels?.copyFailed ?? "Couldn't copy");
2090
+ if (!text) return { ...s, copyNote: failed() };
2091
+ // No clipboard on this host: say so, rather than report a success that
2092
+ // never happened. Same rule as the throw below.
2093
+ if (!o.copyText) return { ...s, copyNote: failed() };
2094
+ // Read BEFORE the await, compared after: `s` is a pre-await snapshot,
2095
+ // and the runtime commits the whole returned state. Two cases.
2096
+ // - A send or + moved the turn boundary: the note is about a reply that
2097
+ // is gone, so stand down and commit nothing (as `stop` does).
2098
+ // - Anything else landed — a keystroke, a widget selection, a query —
2099
+ // so `{ ...s }` would silently undo it. Put the note on the newest
2100
+ // COMMITTED state instead of on the snapshot.
2101
+ const seq = turnSeq;
2102
+ const at = commits;
2103
+ let note: string;
2104
+ try {
2105
+ await o.copyText(text);
2106
+ note = res(o.labels?.copied ?? "Copied");
2107
+ } catch {
2108
+ // The web Async Clipboard API rejects without a user gesture, and
2109
+ // Android refuses while the app is unfocused. Both land here, and both
2110
+ // are a FAILURE the owner is told about.
2111
+ note = failed();
2112
+ }
2113
+ if (turnSeq !== seq) return undefined;
2114
+ // No separate guard for a resume: `load` resumes only when the screen
2115
+ // holds no live conversation, and a Copy row exists only under a reply
2116
+ // in one — so a resume cannot land while a Copy is out. (The nav input
2117
+ // and site are fixed for a worker's life; a site switch boots a new one.)
2118
+ return { ...(commits !== at && committed ? committed : s), copyNote: note };
2119
+ },
2120
+
2121
+ regenerate: async (s) => {
2122
+ // Refuse a double-tap, and refuse while a reply is still coming — there
2123
+ // is nothing to regenerate until the last one has landed.
2124
+ // `_pending` rather than `turnInFlight`, and the difference is load-bearing.
2125
+ // `turnInFlight` is `status === "sending" || !!_pending`, and `status` can
2126
+ // still read "sending" after a turn has actually closed — a tick whose read
2127
+ // straddled a turn boundary returns `undefined` and writes nothing back, so
2128
+ // the status it would have cleared stays put. Measured: with `turnInFlight`
2129
+ // here, the SECOND regenerate of a conversation was refused even though the
2130
+ // first reply had landed. `_pending` is set by `send` and cleared when the
2131
+ // reply arrives, so it tracks "a turn is out" exactly; the latch above
2132
+ // covers the double-tap that `_pending` is too slow for.
2133
+ if (regenerating || !!s._pending) return s;
2134
+ regenerating = true;
2135
+ try {
2136
+ // `send` directly: it is the one send path, and `submit` with no
2137
+ // argument would read the composer instead of sending the retry.
2138
+ return await send(s, REGENERATE_TEXT);
2139
+ } finally {
2140
+ regenerating = false;
2141
+ }
2142
+ },
1975
2143
  };
1976
2144
 
1977
2145
  // Every action's result goes through the Stop gate on its way out.
@@ -1985,6 +2153,8 @@ export function createAriaChat(o: AriaChatOptions): AriaChatCore {
1985
2153
  tick: (s) => impl.tick(s).then((r) => (r ? gated(r) : r)),
1986
2154
  openLink: (s, arg) => impl.openLink(s, arg).then(gated),
1987
2155
  stop: (s) => impl.stop(s).then((r) => (r ? gated(r) : r)),
2156
+ copyMessage: (s, arg) => impl.copyMessage(s, arg).then((r) => (r ? gated(r) : r)),
2157
+ regenerate: (s) => impl.regenerate(s).then(gated),
1988
2158
  // Synchronous, like `typing`: a selection edit makes no network call, so
1989
2159
  // gating it is a plain call rather than a `.then`.
1990
2160
  widgetToggle: (s, arg) => gated(impl.widgetToggle(s, arg)),
@@ -23,6 +23,7 @@
23
23
 
24
24
  import { defineService } from "@himalaya/state";
25
25
  import { open } from "@himalaya/open-url";
26
+ import { writeText } from "@himalaya/clipboard";
26
27
  import {
27
28
  activeSiteId,
28
29
  createAriaChat,
@@ -148,6 +149,11 @@ const core = createAriaChat({
148
149
  typing: () => copyParam("ownerAria.typing", "Aria is typing…"),
149
150
  slow: () => copyParam("ownerAria.slow", "Aria is taking a while…"),
150
151
  stopped: () => copyParam("ownerAria.stopped", "Stopped."),
152
+ // The note beside Copy. Through copyParam for the same reason as the rest:
153
+ // the buttons are localized by the pack, and a note left on the core's
154
+ // English default would be the one line under them that is not.
155
+ copied: () => copyParam("ownerAria.copied", "Copied"),
156
+ copyFailed: () => copyParam("ownerAria.copyFailed", "Couldn't copy"),
151
157
  // Shown when the HOST carries no Wix app binding, so no Wix call can leave
152
158
  // the browser (the web preview). Through copyParam like every other string
153
159
  // here — a default baked into the shared core cannot be localized by this
@@ -167,6 +173,9 @@ const core = createAriaChat({
167
173
  localHistory: false,
168
174
  siteId: activeSiteId,
169
175
  openLink: async (url) => (await open(url, "system_browser")).opened,
176
+ // Resolves or throws — `copyMessage` keys its confirmation on which, so a
177
+ // write the platform refused is never reported as a copy.
178
+ copyText: (text) => writeText(text),
170
179
  resolveEntity: (url) => {
171
180
  const screenId = screenForUrl(url, allowedTargets());
172
181
  return screenId ? { screenId } : null;
@@ -191,6 +200,10 @@ const svc = defineService<AriaChatState, {
191
200
  // new chat) holds a pre-boundary snapshot, and committing it would end the
192
201
  // turn the owner has since started.
193
202
  stop: (s: AriaChatState) => Promise<AriaChatState | undefined>;
203
+ // `undefined` for the same reason again: a write still out when the turn
204
+ // moved on must not commit its pre-await snapshot over the new one.
205
+ copyMessage: (s: AriaChatState, arg?: AriaActionArg) => Promise<AriaChatState | undefined>;
206
+ regenerate: (s: AriaChatState) => Promise<AriaChatState>;
194
207
  widgetPrimary: (s: AriaChatState) => Promise<AriaChatState>;
195
208
  widgetSecondary: (s: AriaChatState) => Promise<AriaChatState>;
196
209
  widgetToggle: (s: AriaChatState, arg?: AriaActionArg) => AriaChatState;
@@ -218,6 +231,8 @@ const svc = defineService<AriaChatState, {
218
231
  // false — so this is only ever dispatched while a reply is outstanding and
219
232
  // the worker is holding the handle that can abort it.
220
233
  stop: core.stop,
234
+ copyMessage: core.copyMessage,
235
+ regenerate: core.regenerate,
221
236
  // The active widget's footer buttons. Named here as well as in the core:
222
237
  // the descriptor dispatches `tier5.<service>.<action>`, and an action the
223
238
  // entry does not export is a control that renders and does nothing.
@@ -37,7 +37,7 @@ export default defineApp({
37
37
  actions: "http://localhost:8190/v1/actions",
38
38
  sync: "http://localhost:8190/v1/sync",
39
39
  },
40
- capabilities: ["live_activity","push","share","secure_storage","open_url","local_database"],
40
+ capabilities: ["live_activity","push","share","secure_storage","open_url","local_database","clipboard"],
41
41
  designTokensPath: "./tokens.json",
42
42
  navigation: {
43
43
  "navKind": "responsive",
@@ -54809,6 +54809,83 @@ const SCREENS = runtimeSetting({
54809
54809
  }
54810
54810
  }
54811
54811
  },
54812
+ {
54813
+ "id": "himi_owner_lite_aria.reply_actions",
54814
+ "body": {
54815
+ "package": "himalaya",
54816
+ "type": "HStack",
54817
+ "props": {
54818
+ "spacing": 4,
54819
+ "children": [
54820
+ {
54821
+ "id": "himi_owner_lite_aria.copy",
54822
+ "body": {
54823
+ "package": "himalaya",
54824
+ "type": "Button",
54825
+ "props": {
54826
+ "title": "Copy",
54827
+ "style": "plain",
54828
+ "size": "sm",
54829
+ "fullWidth": false,
54830
+ "leadingIcon": "copy",
54831
+ "action": {
54832
+ "actionId": "tier5.himiOwnerLiteAria.copyMessage"
54833
+ }
54834
+ }
54835
+ }
54836
+ },
54837
+ {
54838
+ "id": "himi_owner_lite_aria.regenerate",
54839
+ "body": {
54840
+ "package": "himalaya",
54841
+ "type": "Button",
54842
+ "props": {
54843
+ "title": "Regenerate",
54844
+ "style": "plain",
54845
+ "size": "sm",
54846
+ "fullWidth": false,
54847
+ "leadingIcon": "refresh",
54848
+ "action": {
54849
+ "actionId": "tier5.himiOwnerLiteAria.regenerate"
54850
+ }
54851
+ }
54852
+ }
54853
+ },
54854
+ {
54855
+ "id": "",
54856
+ "body": {
54857
+ "package": "himalaya",
54858
+ "type": "Text",
54859
+ "props": {
54860
+ "text": "$service:himiOwnerLiteAria.copyNote",
54861
+ "style": "footnote",
54862
+ "color": "textSecondary",
54863
+ "maxLines": 1,
54864
+ "collapseWhenEmpty": true
54865
+ }
54866
+ }
54867
+ },
54868
+ {
54869
+ "id": "",
54870
+ "body": {
54871
+ "package": "himalaya",
54872
+ "type": "Spacer",
54873
+ "props": {}
54874
+ }
54875
+ }
54876
+ ]
54877
+ }
54878
+ },
54879
+ "modifiers": {
54880
+ "padding": {
54881
+ "leading": 0,
54882
+ "trailing": 0,
54883
+ "top": 0,
54884
+ "bottom": 4
54885
+ },
54886
+ "hiddenPath": "$service:himiOwnerLiteAria.replyActionsHidden"
54887
+ }
54888
+ },
54812
54889
  {
54813
54890
  "id": "himi_owner_lite_aria.prompts.bound",
54814
54891
  "body": {
@@ -58188,7 +58265,8 @@ const SCREENS = runtimeSetting({
58188
58265
  "contentType": "application/javascript+ts-bundle",
58189
58266
  "declaredCapabilities": [
58190
58267
  "wix_api",
58191
- "open_url"
58268
+ "open_url",
58269
+ "clipboard"
58192
58270
  ],
58193
58271
  "minBinaryVersion": "0.1.0",
58194
58272
  "bundleVersion": "0.0.1"