@hitch42/applet 0.1.0-beta.10 → 0.1.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/applet.js +11 -11
- package/dist/{build-BlmLiKro.js → build-BRpYQ9Lk.js} +1 -1
- package/dist/build-CypJADhV.js +2 -0
- package/dist/{ci-0Q2mUh53.js → ci-C2CL-0sj.js} +2 -2
- package/dist/client.d.ts +2 -2
- package/dist/{contract-QUvEGiRI.d.ts → contract-CsUpTBe0.d.ts} +1 -1
- package/dist/{deploy-BbL3GObV.js → deploy-CdQQQ5HG.js} +131 -80
- package/dist/{generate-XB-72Fzj.js → generate-BDHb2kjw.js} +7 -4
- package/dist/generate-Cai2X9z1.js +2 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/{init-DwsXRUk-.js → init-CpVfJQbv.js} +4 -4
- package/dist/{load-config-OKZU3UKn.js → load-config-DI-e1Ool.js} +1 -1
- package/dist/{logs-XvK6Zs5Y.js → logs-CffdKMRd.js} +2 -2
- package/dist/{manifest-I-9s8R36.d.ts → manifest-C6_F4bL8.d.ts} +3 -1
- package/dist/{manifest-4ZAc6HUk.js → manifest-CJSL9GuC.js} +6 -1
- package/dist/{project-B0qNXnFu.js → project-D-hX59RS.js} +4 -1
- package/dist/react.d.ts +2 -1
- package/dist/react.js +57 -37
- package/dist/{schedules-BkWZsbSs.js → schedules-DIIowhD3.js} +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/telephony.d.ts +24 -4
- package/dist/telephony.js +135 -16
- package/dist/{variables-0tjnAeut.js → variables-Bzl2ZLQN.js} +1 -1
- package/dist/vite.d.ts +1 -1
- package/dist/vite.js +17 -6
- package/package.json +3 -1
- package/scaffold/.agents/skills/hitch-applets/SKILL.md +1 -1
- package/scaffold/.agents/skills/hitch-applets/references/markdown.md +2 -0
- package/scaffold/.agents/skills/hitch-theory/SKILL.md +1 -1
- package/scaffold/.github/workflows/hitch.yml +1 -1
- package/scaffold/applet/src/client/styles/globals.css +2 -0
- package/dist/build-CcT-wNN9.js +0 -2
- package/dist/generate-wIN3IYqL.js +0 -2
package/dist/react.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { BetweenHorizontalEnd, BetweenVerticalEnd, Bold, Code, Heading, Italic, Link, List, ListMinus, ListOrdered, ListTodo, Quote, Strikethrough, Table, Trash2 } from "lucide-react";
|
|
2
|
+
import { BetweenHorizontalEnd, BetweenVerticalEnd, Bold, Code, Heading, Italic, Link, List, ListMinus, ListOrdered, ListTodo, Quote, Strikethrough, Table, Trash2, X } from "lucide-react";
|
|
3
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
import { Streamdown, defaultRehypePlugins, defaultUrlTransform } from "streamdown";
|
|
5
|
+
import { GapCursor } from "@tiptap/pm/gapcursor";
|
|
6
|
+
import { NodeSelection } from "@tiptap/pm/state";
|
|
5
7
|
import { EditorContent, NodeViewWrapper, ReactNodeViewRenderer, useEditor, useEditorState } from "@tiptap/react";
|
|
6
8
|
import { Button, Toolbar } from "react-aria-components";
|
|
7
9
|
import { twMerge } from "cn";
|
|
@@ -123,11 +125,14 @@ function Markdown({ children, files, className }) {
|
|
|
123
125
|
function sanitizeAltText(text) {
|
|
124
126
|
return text.replaceAll(/[[\]()\\]/g, "").replaceAll(/\s+/g, " ");
|
|
125
127
|
}
|
|
128
|
+
function editorMarkdown(editor) {
|
|
129
|
+
return editor.getMarkdown().trimEnd();
|
|
130
|
+
}
|
|
126
131
|
function tableHasBlockCells(node) {
|
|
127
132
|
return (node.content ?? []).some((row) => (row.content ?? []).some((cell) => cell.content?.length !== 1 || cell.content[0]?.type !== "paragraph"));
|
|
128
133
|
}
|
|
129
134
|
function hitchImageNode(files) {
|
|
130
|
-
return function HitchImageNode({ node }) {
|
|
135
|
+
return function HitchImageNode({ node, selected, editor, deleteNode }) {
|
|
131
136
|
const src = String(node.attrs.src);
|
|
132
137
|
const alt = typeof node.attrs.alt === "string" ? node.attrs.alt : void 0;
|
|
133
138
|
const id = src.startsWith("hitch-file://") ? src.slice(13) : void 0;
|
|
@@ -152,42 +157,46 @@ function hitchImageNode(files) {
|
|
|
152
157
|
active = false;
|
|
153
158
|
};
|
|
154
159
|
}, [files, id]);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
"data-drag-handle": true
|
|
162
|
-
})
|
|
163
|
-
});
|
|
164
|
-
if (files === void 0 || state.error !== void 0) return /* @__PURE__ */ jsx(NodeViewWrapper, {
|
|
165
|
-
as: "span",
|
|
160
|
+
const content = id === void 0 ? /* @__PURE__ */ jsx("img", {
|
|
161
|
+
src,
|
|
162
|
+
alt,
|
|
163
|
+
className: "max-w-full rounded-lg",
|
|
164
|
+
"data-drag-handle": true
|
|
165
|
+
}) : files === void 0 || state.error !== void 0 ? /* @__PURE__ */ jsx("span", {
|
|
166
166
|
className: "text-muted-foreground text-sm",
|
|
167
167
|
children: alt ?? "Image unavailable"
|
|
168
|
-
})
|
|
169
|
-
if (state.id !== id || state.url === void 0) return /* @__PURE__ */ jsx(NodeViewWrapper, {
|
|
170
|
-
as: "span",
|
|
168
|
+
}) : state.id !== id || state.url === void 0 ? /* @__PURE__ */ jsx("span", {
|
|
171
169
|
className: "inline-block min-h-16 min-w-32 animate-pulse rounded-md bg-muted p-3 text-muted-foreground text-sm",
|
|
172
170
|
role: "img",
|
|
173
171
|
"aria-label": alt ?? "Loading image",
|
|
174
172
|
children: alt ?? "Loading image"
|
|
173
|
+
}) : /* @__PURE__ */ jsx("img", {
|
|
174
|
+
src: state.url,
|
|
175
|
+
alt,
|
|
176
|
+
className: "max-w-full rounded-lg",
|
|
177
|
+
"data-drag-handle": true,
|
|
178
|
+
onError: () => {
|
|
179
|
+
invalidateDownloadUrl(id);
|
|
180
|
+
setState({
|
|
181
|
+
id,
|
|
182
|
+
error: /* @__PURE__ */ new Error("image failed to load")
|
|
183
|
+
});
|
|
184
|
+
}
|
|
175
185
|
});
|
|
176
|
-
return /* @__PURE__ */
|
|
186
|
+
return /* @__PURE__ */ jsxs(NodeViewWrapper, {
|
|
177
187
|
as: "span",
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
"
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
})
|
|
188
|
+
className: twMerge("relative inline-block max-w-full rounded-lg", selected && "in-[.ProseMirror[contenteditable=true]:focus-within]:ring-2 in-[.ProseMirror[contenteditable=true]:focus-within]:ring-ring"),
|
|
189
|
+
children: [content, selected && /* @__PURE__ */ jsx("button", {
|
|
190
|
+
type: "button",
|
|
191
|
+
contentEditable: false,
|
|
192
|
+
"aria-label": "Remove image",
|
|
193
|
+
className: "absolute top-1.5 right-1.5 hidden size-6 in-[.ProseMirror[contenteditable=true]:focus-within]:inline-flex items-center justify-center rounded-full bg-background/90 text-foreground shadow-sm outline-none hover:bg-muted focus-visible:ring-3 focus-visible:ring-ring/30 [&_svg]:size-3.5",
|
|
194
|
+
onClick: () => {
|
|
195
|
+
deleteNode();
|
|
196
|
+
editor.commands.focus();
|
|
197
|
+
},
|
|
198
|
+
children: /* @__PURE__ */ jsx(X, {})
|
|
199
|
+
})]
|
|
191
200
|
});
|
|
192
201
|
};
|
|
193
202
|
}
|
|
@@ -223,7 +232,7 @@ function findImage(view, src) {
|
|
|
223
232
|
});
|
|
224
233
|
return found;
|
|
225
234
|
}
|
|
226
|
-
function MarkdownEditor({ value, onChange, files, id, placeholder, required, minLength, maxLength, disabled, className, onUploadError }) {
|
|
235
|
+
function MarkdownEditor({ value, onChange, files, id, placeholder, required, minLength, maxLength, disabled, autoFocus, className, onUploadError }) {
|
|
227
236
|
const validationRef = useRef(null);
|
|
228
237
|
const lastEmittedRef = useRef(value);
|
|
229
238
|
const filesRef = useRef(files);
|
|
@@ -260,6 +269,7 @@ function MarkdownEditor({ value, onChange, files, id, placeholder, required, min
|
|
|
260
269
|
content: value,
|
|
261
270
|
contentType: "markdown",
|
|
262
271
|
editable: !disabled,
|
|
272
|
+
autofocus: autoFocus ? "end" : false,
|
|
263
273
|
editorProps: {
|
|
264
274
|
attributes: { class: editorClassName },
|
|
265
275
|
handlePaste: (view, event) => {
|
|
@@ -280,8 +290,17 @@ function MarkdownEditor({ value, onChange, files, id, placeholder, required, min
|
|
|
280
290
|
return true;
|
|
281
291
|
}
|
|
282
292
|
},
|
|
283
|
-
|
|
284
|
-
|
|
293
|
+
onCreate: ({ editor: createdEditor }) => {
|
|
294
|
+
if (autoFocus) createdEditor.commands.focus("end");
|
|
295
|
+
else if (createdEditor.state.selection instanceof NodeSelection) {
|
|
296
|
+
const { state, view } = createdEditor;
|
|
297
|
+
view.dispatch(state.tr.setSelection(new GapCursor(state.doc.resolve(0))));
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
onUpdate: ({ editor: updatedEditor, transaction }) => {
|
|
301
|
+
if (!transaction.docChanged) return;
|
|
302
|
+
const markdown = editorMarkdown(updatedEditor);
|
|
303
|
+
if (markdown === lastEmittedRef.current) return;
|
|
285
304
|
lastEmittedRef.current = markdown;
|
|
286
305
|
onChange(markdown);
|
|
287
306
|
}
|
|
@@ -327,10 +346,11 @@ function MarkdownEditor({ value, onChange, files, id, placeholder, required, min
|
|
|
327
346
|
}
|
|
328
347
|
}
|
|
329
348
|
useEffect(() => {
|
|
330
|
-
if (value !== lastEmittedRef.current && value !== editor
|
|
349
|
+
if (value !== lastEmittedRef.current && value !== editorMarkdown(editor)) editor.commands.setContent(value, {
|
|
331
350
|
contentType: "markdown",
|
|
332
351
|
emitUpdate: false
|
|
333
352
|
});
|
|
353
|
+
lastEmittedRef.current = value;
|
|
334
354
|
}, [editor, value]);
|
|
335
355
|
useEffect(() => editor.setEditable(!disabled, false), [disabled, editor]);
|
|
336
356
|
const active = useEditorState({
|
|
@@ -474,10 +494,11 @@ function MarkdownEditor({ value, onChange, files, id, placeholder, required, min
|
|
|
474
494
|
}
|
|
475
495
|
];
|
|
476
496
|
return /* @__PURE__ */ jsxs("div", {
|
|
477
|
-
className: twMerge("overflow-hidden rounded-2xl border border-input bg-background", className),
|
|
497
|
+
className: twMerge("flex flex-col overflow-hidden rounded-2xl border border-input bg-background", className),
|
|
478
498
|
children: [
|
|
499
|
+
/* @__PURE__ */ jsx(EditorContent, { editor }),
|
|
479
500
|
/* @__PURE__ */ jsxs(Toolbar, {
|
|
480
|
-
className: "flex flex-wrap gap-1 border-b border-input p-1",
|
|
501
|
+
className: "order-first flex flex-wrap gap-1 border-b border-input p-1",
|
|
481
502
|
"aria-label": "Markdown formatting",
|
|
482
503
|
children: [items.map(({ title, active: isActive, icon: Icon, command }) => /* @__PURE__ */ jsx(Button, {
|
|
483
504
|
className: twMerge(buttonClassName, isActive && "bg-muted text-foreground"),
|
|
@@ -496,7 +517,6 @@ function MarkdownEditor({ value, onChange, files, id, placeholder, required, min
|
|
|
496
517
|
children: /* @__PURE__ */ jsx(Icon, { className: iconClassName })
|
|
497
518
|
}, title))] })]
|
|
498
519
|
}),
|
|
499
|
-
/* @__PURE__ */ jsx(EditorContent, { editor }),
|
|
500
520
|
/* @__PURE__ */ jsx("textarea", {
|
|
501
521
|
ref: validationRef,
|
|
502
522
|
id,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as loadAppletConfig } from "./load-config-
|
|
1
|
+
import { t as loadAppletConfig } from "./load-config-DI-e1Ool.js";
|
|
2
2
|
import { t as question } from "./prompt-ChS-grYO.js";
|
|
3
3
|
import { RPCLink } from "@orpc/client/fetch";
|
|
4
4
|
import { readFile } from "node:fs/promises";
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as createWorkflow, i as WorkflowStep, n as WorkflowDefinition, r as WorkflowDuration } from "./workflow-DurM6Fwx.js";
|
|
2
|
-
import { _ as RegisteredWorkflows, f as RegisteredContract, g as RegisteredVariables, p as RegisteredManifest } from "./manifest-
|
|
3
|
-
import { t as NarrowContract } from "./contract-
|
|
2
|
+
import { _ as RegisteredWorkflows, f as RegisteredContract, g as RegisteredVariables, p as RegisteredManifest } from "./manifest-C6_F4bL8.js";
|
|
3
|
+
import { t as NarrowContract } from "./contract-CsUpTBe0.js";
|
|
4
4
|
import { t as WithBatchRecorder } from "./batch-Qa4B4-Yp.js";
|
|
5
5
|
import { o as WithFilesUpload } from "./upload-DKZzz5kW.js";
|
|
6
6
|
import { ContractRouterClient } from "@orpc/contract";
|
package/dist/telephony.d.ts
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
//#region src/telephony/phone.d.ts
|
|
2
2
|
type PhoneState = {
|
|
3
|
+
owner: boolean;
|
|
3
4
|
status: "off" | "connecting" | "ready" | "error";
|
|
4
5
|
error: string | null;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
incoming: null | {
|
|
7
|
+
id: string;
|
|
8
|
+
from: string;
|
|
9
|
+
};
|
|
10
|
+
active: null | ({
|
|
8
11
|
from: string;
|
|
9
12
|
state: "connecting" | "ringing" | "active" | "held" | "ended";
|
|
10
13
|
muted: boolean;
|
|
11
14
|
startedAt: number;
|
|
15
|
+
} & ({
|
|
16
|
+
direction: "outbound";
|
|
17
|
+
callId: string;
|
|
18
|
+
to: string;
|
|
12
19
|
recording: boolean;
|
|
13
|
-
}
|
|
20
|
+
} | {
|
|
21
|
+
direction: "inbound";
|
|
22
|
+
recording: false;
|
|
23
|
+
}));
|
|
14
24
|
};
|
|
15
25
|
type CallInput = {
|
|
16
26
|
numberId: string;
|
|
@@ -31,6 +41,8 @@ declare class Phone {
|
|
|
31
41
|
private readonly api;
|
|
32
42
|
private client;
|
|
33
43
|
private currentCall;
|
|
44
|
+
private incomingCall;
|
|
45
|
+
private readonly acceptsCalls;
|
|
34
46
|
private connectPromise;
|
|
35
47
|
private releaseLock;
|
|
36
48
|
private lockAbortController;
|
|
@@ -44,8 +56,11 @@ declare class Phone {
|
|
|
44
56
|
private audio;
|
|
45
57
|
private firstLogin;
|
|
46
58
|
private callStarting;
|
|
59
|
+
private reconnectOnPageShow;
|
|
60
|
+
private connectionRequested;
|
|
47
61
|
constructor(options?: {
|
|
48
62
|
baseUrl?: string;
|
|
63
|
+
acceptsCalls?: boolean;
|
|
49
64
|
});
|
|
50
65
|
subscribe: (listener: Listener) => (() => void);
|
|
51
66
|
getSnapshot: () => PhoneState;
|
|
@@ -55,8 +70,12 @@ declare class Phone {
|
|
|
55
70
|
private connectInternal;
|
|
56
71
|
private connectOwned;
|
|
57
72
|
disconnect: () => void;
|
|
73
|
+
private pageHide;
|
|
74
|
+
private pageShow;
|
|
58
75
|
private beforeUnload;
|
|
59
76
|
call: ({ numberId, to }: CallInput) => Promise<void>;
|
|
77
|
+
answer: () => Promise<void>;
|
|
78
|
+
decline: () => Promise<void>;
|
|
60
79
|
private sendCommand;
|
|
61
80
|
private onChannelMessage;
|
|
62
81
|
private runCommand;
|
|
@@ -65,6 +84,7 @@ declare class Phone {
|
|
|
65
84
|
}
|
|
66
85
|
declare const createPhone: (options?: {
|
|
67
86
|
baseUrl?: string;
|
|
87
|
+
acceptsCalls?: boolean;
|
|
68
88
|
}) => Phone;
|
|
69
89
|
//#endregion
|
|
70
90
|
//#region src/telephony/use-phone.d.ts
|
package/dist/telephony.js
CHANGED
|
@@ -28,9 +28,12 @@ const createTelephonyApi = (baseUrl = location.origin) => {
|
|
|
28
28
|
return response.json();
|
|
29
29
|
};
|
|
30
30
|
return {
|
|
31
|
-
token: (deviceKey) => request("voice/token", {
|
|
31
|
+
token: (deviceKey, acceptsCalls) => request("voice/token", {
|
|
32
32
|
method: "POST",
|
|
33
|
-
body: JSON.stringify({
|
|
33
|
+
body: JSON.stringify({
|
|
34
|
+
deviceKey,
|
|
35
|
+
acceptsCalls
|
|
36
|
+
})
|
|
34
37
|
}),
|
|
35
38
|
startCall: (numberId, to) => request("calls", {
|
|
36
39
|
method: "POST",
|
|
@@ -165,14 +168,18 @@ const DEVICE_KEY = "hitch:telephony:device";
|
|
|
165
168
|
let singleton;
|
|
166
169
|
var Phone = class {
|
|
167
170
|
state = {
|
|
171
|
+
owner: false,
|
|
168
172
|
status: "off",
|
|
169
173
|
error: null,
|
|
174
|
+
incoming: null,
|
|
170
175
|
active: null
|
|
171
176
|
};
|
|
172
177
|
listeners = /* @__PURE__ */ new Set();
|
|
173
178
|
api;
|
|
174
179
|
client = null;
|
|
175
180
|
currentCall = null;
|
|
181
|
+
incomingCall = null;
|
|
182
|
+
acceptsCalls;
|
|
176
183
|
connectPromise = null;
|
|
177
184
|
releaseLock = null;
|
|
178
185
|
lockAbortController = null;
|
|
@@ -186,8 +193,11 @@ var Phone = class {
|
|
|
186
193
|
audio = null;
|
|
187
194
|
firstLogin = true;
|
|
188
195
|
callStarting = false;
|
|
196
|
+
reconnectOnPageShow = false;
|
|
197
|
+
connectionRequested = false;
|
|
189
198
|
constructor(options = {}) {
|
|
190
199
|
this.api = createTelephonyApi(options.baseUrl);
|
|
200
|
+
this.acceptsCalls = options.acceptsCalls ?? false;
|
|
191
201
|
}
|
|
192
202
|
subscribe = (listener) => {
|
|
193
203
|
this.listeners.add(listener);
|
|
@@ -248,6 +258,7 @@ var Phone = class {
|
|
|
248
258
|
};
|
|
249
259
|
}
|
|
250
260
|
connect = () => {
|
|
261
|
+
this.connectionRequested = true;
|
|
251
262
|
this.connectPromise ??= this.connectInternal().finally(() => {
|
|
252
263
|
this.connectPromise = null;
|
|
253
264
|
});
|
|
@@ -255,6 +266,8 @@ var Phone = class {
|
|
|
255
266
|
};
|
|
256
267
|
async connectInternal() {
|
|
257
268
|
if (this.state.status === "ready") return;
|
|
269
|
+
window.addEventListener("pagehide", this.pageHide);
|
|
270
|
+
window.addEventListener("pageshow", this.pageShow);
|
|
258
271
|
this.channel ??= new BroadcastChannel("hitch:telephony");
|
|
259
272
|
this.channel.onmessage = this.onChannelMessage;
|
|
260
273
|
this.update({
|
|
@@ -275,8 +288,10 @@ var Phone = class {
|
|
|
275
288
|
});
|
|
276
289
|
this.ownsPhone = true;
|
|
277
290
|
this.update({
|
|
291
|
+
owner: true,
|
|
278
292
|
status: "connecting",
|
|
279
293
|
error: null,
|
|
294
|
+
incoming: null,
|
|
280
295
|
active: null
|
|
281
296
|
});
|
|
282
297
|
await this.connectOwned();
|
|
@@ -297,6 +312,7 @@ var Phone = class {
|
|
|
297
312
|
return;
|
|
298
313
|
}
|
|
299
314
|
this.ownsPhone = true;
|
|
315
|
+
this.update({ owner: true });
|
|
300
316
|
await this.connectOwned();
|
|
301
317
|
}
|
|
302
318
|
async connectOwned() {
|
|
@@ -311,7 +327,7 @@ var Phone = class {
|
|
|
311
327
|
8e3
|
|
312
328
|
]) {
|
|
313
329
|
if (delay) await new Promise((resolve) => setTimeout(resolve, delay));
|
|
314
|
-
const { token } = await this.api.token(deviceKey);
|
|
330
|
+
const { token } = await this.api.token(deviceKey, this.acceptsCalls);
|
|
315
331
|
const { TelnyxRTC: Client } = await import("@telnyx/webrtc");
|
|
316
332
|
const client = new Client({
|
|
317
333
|
login_token: token,
|
|
@@ -320,15 +336,35 @@ var Phone = class {
|
|
|
320
336
|
this.client = client;
|
|
321
337
|
client.on("telnyx.notification", ({ type, call }) => {
|
|
322
338
|
if (type !== "callUpdate" || !call) return;
|
|
323
|
-
if (call
|
|
324
|
-
|
|
339
|
+
if (call === this.currentCall) {
|
|
340
|
+
this.onCallUpdate(call);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (call.direction !== "inbound") return;
|
|
344
|
+
if ([
|
|
345
|
+
"hangup",
|
|
346
|
+
"destroy",
|
|
347
|
+
"purge"
|
|
348
|
+
].includes(call.state)) {
|
|
349
|
+
if (call === this.incomingCall) {
|
|
350
|
+
this.incomingCall = null;
|
|
351
|
+
this.update({ incoming: null });
|
|
352
|
+
}
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
if (!this.acceptsCalls || this.currentCall || this.callStarting || this.incomingCall && this.incomingCall !== call) {
|
|
356
|
+
call.hangup().catch((error) => console.error("Call decline failed", error));
|
|
325
357
|
return;
|
|
326
358
|
}
|
|
327
|
-
|
|
359
|
+
this.incomingCall = call;
|
|
360
|
+
if (call.state === "ringing") this.update({ incoming: {
|
|
361
|
+
id: call.id,
|
|
362
|
+
from: call.options.remoteCallerNumber ?? ""
|
|
363
|
+
} });
|
|
328
364
|
});
|
|
329
365
|
client.on("telnyx.warning", (warning) => {
|
|
330
366
|
if (Number(warning.warning.code) !== 34001) return;
|
|
331
|
-
this.api.token(deviceKey).then(({ token: nextToken }) => client.login({ creds: { login_token: nextToken } })).catch((error) => {
|
|
367
|
+
this.api.token(deviceKey, this.acceptsCalls).then(({ token: nextToken }) => client.login({ creds: { login_token: nextToken } })).catch((error) => {
|
|
332
368
|
console.error("Phone token renewal failed", error);
|
|
333
369
|
this.update({ error: String(error) });
|
|
334
370
|
});
|
|
@@ -341,7 +377,6 @@ var Phone = class {
|
|
|
341
377
|
});
|
|
342
378
|
this.firstLogin = false;
|
|
343
379
|
this.update({ status: "ready" });
|
|
344
|
-
window.addEventListener("pagehide", this.disconnect);
|
|
345
380
|
return;
|
|
346
381
|
} catch (error) {
|
|
347
382
|
await client.disconnect().catch((disconnectError) => console.error("Phone disconnect failed", disconnectError));
|
|
@@ -351,6 +386,7 @@ var Phone = class {
|
|
|
351
386
|
}
|
|
352
387
|
} catch (error) {
|
|
353
388
|
this.update({
|
|
389
|
+
owner: false,
|
|
354
390
|
status: "error",
|
|
355
391
|
error: error instanceof Error ? error.message : String(error)
|
|
356
392
|
});
|
|
@@ -360,13 +396,16 @@ var Phone = class {
|
|
|
360
396
|
}
|
|
361
397
|
}
|
|
362
398
|
disconnect = () => {
|
|
399
|
+
this.connectionRequested = false;
|
|
363
400
|
this.lockAbortController?.abort();
|
|
364
401
|
this.lockAbortController = null;
|
|
365
402
|
this.connectResolve?.();
|
|
366
403
|
this.connectResolve = null;
|
|
367
404
|
if (this.pollTimer) clearTimeout(this.pollTimer);
|
|
368
405
|
this.pollTimer = null;
|
|
369
|
-
window.removeEventListener("pagehide", this.
|
|
406
|
+
window.removeEventListener("pagehide", this.pageHide);
|
|
407
|
+
window.removeEventListener("pageshow", this.pageShow);
|
|
408
|
+
this.reconnectOnPageShow = false;
|
|
370
409
|
window.removeEventListener("beforeunload", this.beforeUnload);
|
|
371
410
|
this.client?.disconnect().catch((error) => console.error("Phone disconnect failed", error));
|
|
372
411
|
this.client = null;
|
|
@@ -377,20 +416,35 @@ var Phone = class {
|
|
|
377
416
|
this.pendingCommands.clear();
|
|
378
417
|
this.releaseLock?.();
|
|
379
418
|
this.releaseLock = null;
|
|
419
|
+
this.currentCall = null;
|
|
420
|
+
this.incomingCall = null;
|
|
380
421
|
this.update({
|
|
422
|
+
owner: false,
|
|
381
423
|
status: "off",
|
|
424
|
+
incoming: null,
|
|
382
425
|
active: null
|
|
383
426
|
});
|
|
384
427
|
this.ownsPhone = false;
|
|
385
428
|
this.channel?.close();
|
|
386
429
|
this.channel = null;
|
|
387
430
|
};
|
|
431
|
+
pageHide = () => {
|
|
432
|
+
const reconnect = this.connectionRequested;
|
|
433
|
+
this.disconnect();
|
|
434
|
+
this.reconnectOnPageShow = reconnect;
|
|
435
|
+
if (reconnect) window.addEventListener("pageshow", this.pageShow);
|
|
436
|
+
};
|
|
437
|
+
pageShow = (event) => {
|
|
438
|
+
if (!event.persisted || !this.reconnectOnPageShow) return;
|
|
439
|
+
this.reconnectOnPageShow = false;
|
|
440
|
+
this.connect();
|
|
441
|
+
};
|
|
388
442
|
beforeUnload = (event) => {
|
|
389
443
|
event.preventDefault();
|
|
390
444
|
event.returnValue = "";
|
|
391
445
|
};
|
|
392
446
|
call = async ({ numberId, to }) => {
|
|
393
|
-
if (this.callStarting || this.state.active) throw new Error("A call is already in progress.");
|
|
447
|
+
if (this.callStarting || this.state.active || this.incomingCall || this.state.incoming) throw new Error("A call is already in progress.");
|
|
394
448
|
if (this.state.status !== "ready") throw new Error("The phone is not ready.");
|
|
395
449
|
if (!this.ownsPhone) return this.sendCommand("call", {
|
|
396
450
|
numberId,
|
|
@@ -417,6 +471,7 @@ var Phone = class {
|
|
|
417
471
|
this.update({
|
|
418
472
|
error: null,
|
|
419
473
|
active: {
|
|
474
|
+
direction: "outbound",
|
|
420
475
|
callId,
|
|
421
476
|
to,
|
|
422
477
|
from: callerNumber,
|
|
@@ -432,6 +487,62 @@ var Phone = class {
|
|
|
432
487
|
this.callStarting = false;
|
|
433
488
|
}
|
|
434
489
|
};
|
|
490
|
+
answer = async () => {
|
|
491
|
+
if (!this.state.incoming) throw new Error("There is no incoming call.");
|
|
492
|
+
if (!this.ownsPhone) return this.sendCommand("answer", this.state.incoming.id);
|
|
493
|
+
const call = this.incomingCall;
|
|
494
|
+
if (!call) throw new Error("There is no incoming call.");
|
|
495
|
+
if (!this.client) throw new Error("The phone is not ready.");
|
|
496
|
+
this.audio ??= document.createElement("audio");
|
|
497
|
+
this.audio.autoplay = true;
|
|
498
|
+
this.audio.hidden = true;
|
|
499
|
+
if (!this.audio.isConnected) document.body.append(this.audio);
|
|
500
|
+
this.client.remoteElement = this.audio;
|
|
501
|
+
call.options.remoteElement = this.audio;
|
|
502
|
+
this.currentCall = call;
|
|
503
|
+
this.incomingCall = null;
|
|
504
|
+
this.update({
|
|
505
|
+
incoming: null,
|
|
506
|
+
error: null,
|
|
507
|
+
active: {
|
|
508
|
+
direction: "inbound",
|
|
509
|
+
from: this.state.incoming.from,
|
|
510
|
+
state: "connecting",
|
|
511
|
+
muted: false,
|
|
512
|
+
startedAt: Date.now(),
|
|
513
|
+
recording: false
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
window.addEventListener("beforeunload", this.beforeUnload);
|
|
517
|
+
try {
|
|
518
|
+
await call.answer();
|
|
519
|
+
} catch (error) {
|
|
520
|
+
if (this.currentCall === call) {
|
|
521
|
+
this.currentCall = null;
|
|
522
|
+
this.incomingCall = call;
|
|
523
|
+
this.update({
|
|
524
|
+
active: null,
|
|
525
|
+
incoming: {
|
|
526
|
+
id: call.id,
|
|
527
|
+
from: call.options.remoteCallerNumber ?? ""
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
window.removeEventListener("beforeunload", this.beforeUnload);
|
|
531
|
+
}
|
|
532
|
+
throw error;
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
decline = async () => {
|
|
536
|
+
if (!this.state.incoming) throw new Error("There is no incoming call.");
|
|
537
|
+
if (!this.ownsPhone) return this.sendCommand("decline", this.state.incoming.id);
|
|
538
|
+
const call = this.incomingCall;
|
|
539
|
+
if (!call) throw new Error("There is no incoming call.");
|
|
540
|
+
await call.hangup();
|
|
541
|
+
if (this.incomingCall === call) {
|
|
542
|
+
this.incomingCall = null;
|
|
543
|
+
this.update({ incoming: null });
|
|
544
|
+
}
|
|
545
|
+
};
|
|
435
546
|
sendCommand(action, value) {
|
|
436
547
|
const id = crypto.randomUUID();
|
|
437
548
|
return new Promise((resolve, reject) => {
|
|
@@ -462,7 +573,10 @@ var Phone = class {
|
|
|
462
573
|
else if (data.type === "state" && !this.ownsPhone && "state" in data) {
|
|
463
574
|
const state = data.state;
|
|
464
575
|
if (typeof state !== "object" || state === null || !("status" in state)) return;
|
|
465
|
-
this.state =
|
|
576
|
+
this.state = {
|
|
577
|
+
...state,
|
|
578
|
+
owner: false
|
|
579
|
+
};
|
|
466
580
|
this.listeners.forEach((listener) => listener());
|
|
467
581
|
this.connectResolve?.();
|
|
468
582
|
this.connectResolve = null;
|
|
@@ -483,6 +597,10 @@ var Phone = class {
|
|
|
483
597
|
}));
|
|
484
598
|
};
|
|
485
599
|
async runCommand(data) {
|
|
600
|
+
if (data.action === "answer" || data.action === "decline") {
|
|
601
|
+
if (!this.incomingCall || data.value !== this.incomingCall.id) throw new Error("That call is no longer ringing.");
|
|
602
|
+
return data.action === "answer" ? this.answer() : this.decline();
|
|
603
|
+
}
|
|
486
604
|
if (data.action === "call") {
|
|
487
605
|
const value = data.value;
|
|
488
606
|
if (!value || typeof value !== "object" || !("numberId" in value) || !("to" in value) || typeof value.numberId !== "string" || typeof value.to !== "string") throw new Error("Invalid call command.");
|
|
@@ -518,7 +636,7 @@ var Phone = class {
|
|
|
518
636
|
}
|
|
519
637
|
}
|
|
520
638
|
async pollCall(callId) {
|
|
521
|
-
if (this.state.active?.callId !== callId || this.state.active.state === "active") return;
|
|
639
|
+
if (this.state.active?.direction !== "outbound" || this.state.active.callId !== callId || this.state.active.state === "active") return;
|
|
522
640
|
try {
|
|
523
641
|
const { status } = await this.api.getCall(callId);
|
|
524
642
|
if ([
|
|
@@ -533,7 +651,8 @@ var Phone = class {
|
|
|
533
651
|
} catch (error) {
|
|
534
652
|
console.error("Call status check failed", error);
|
|
535
653
|
}
|
|
536
|
-
|
|
654
|
+
const active = this.getSnapshot().active;
|
|
655
|
+
if (active?.direction === "outbound" && active.callId === callId) this.pollTimer = setTimeout(() => void this.pollCall(callId), 1e4);
|
|
537
656
|
}
|
|
538
657
|
onCallUpdate(call) {
|
|
539
658
|
const active = this.state.active;
|
|
@@ -562,15 +681,15 @@ var Phone = class {
|
|
|
562
681
|
return;
|
|
563
682
|
}
|
|
564
683
|
const mapped = state === "active" ? "active" : state === "held" ? "held" : state === "ringing" ? "ringing" : "connecting";
|
|
565
|
-
if (mapped === "active" && active.recording && !this.stopRecording && !this.recordingPromise) {
|
|
684
|
+
if (mapped === "active" && active.direction === "outbound" && active.recording && !this.stopRecording && !this.recordingPromise) {
|
|
566
685
|
this.recordingPromise = startRecording(active.callId, call, this.api, (error) => {
|
|
567
686
|
console.error("Call recording failed", error);
|
|
568
|
-
if (this.state.active?.callId === active.callId) this.update({ active: {
|
|
687
|
+
if (this.state.active?.direction === "outbound" && this.state.active.callId === active.callId) this.update({ active: {
|
|
569
688
|
...this.state.active,
|
|
570
689
|
recording: false
|
|
571
690
|
} });
|
|
572
691
|
}).then((stop) => {
|
|
573
|
-
if (this.state.active?.callId === active.callId) this.stopRecording = stop;
|
|
692
|
+
if (this.state.active?.direction === "outbound" && this.state.active.callId === active.callId) this.stopRecording = stop;
|
|
574
693
|
return stop;
|
|
575
694
|
});
|
|
576
695
|
this.recordingPromise.catch((error) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as fetchWithOrgAuth } from "./workspace-DruAZPmV.js";
|
|
2
|
-
import { t as loadAppletConfig } from "./load-config-
|
|
2
|
+
import { t as loadAppletConfig } from "./load-config-DI-e1Ool.js";
|
|
3
3
|
import { t as question } from "./prompt-ChS-grYO.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
//#region src/variables.ts
|
package/dist/vite.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as RegisteredAppletSourceDefinition } from "./manifest-
|
|
1
|
+
import { o as RegisteredAppletSourceDefinition } from "./manifest-C6_F4bL8.js";
|
|
2
2
|
import { PluginOption } from "vite";
|
|
3
3
|
//#region ../shared/auth/src/store.d.mts
|
|
4
4
|
interface SessionCredentials {
|
package/dist/vite.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { n as meSchema } from "./me-BW7Paary.js";
|
|
2
|
-
import { i as APPLET_CODE_PATTERN, n as appletManifestSchema, r as appletSourceDefinitionSchema } from "./manifest-
|
|
2
|
+
import { i as APPLET_CODE_PATTERN, n as appletManifestSchema, r as appletSourceDefinitionSchema } from "./manifest-CJSL9GuC.js";
|
|
3
3
|
import { a as openQueryCacheDatabase } from "./query-cache-CXmAyLv3.js";
|
|
4
4
|
import { a as workflowOutcomeSchema, c as HITCH_SESSION_HEADER, l as HITCH_USER_HEADER, n as runWorkflowJournal } from "./workflow-engine-MogejNfF.js";
|
|
5
5
|
import { c as bearerToken, d as resolveAuthForOrg, f as resolveInstanceUrl, l as organizationInstanceUrl, t as resolveWorkspaceOrg, u as rejectEnvironmentKey } from "./workspace-DruAZPmV.js";
|
|
6
|
-
import { c as writeClientEntry, i as writeRegisterDeclaration, l as writeManifestGen, r as syncContract, s as writeAppletRoot, u as writeServerEntry } from "./generate-
|
|
6
|
+
import { c as writeClientEntry, i as writeRegisterDeclaration, l as writeManifestGen, r as syncContract, s as writeAppletRoot, u as writeServerEntry } from "./generate-BDHb2kjw.js";
|
|
7
7
|
import { randomUUID } from "node:crypto";
|
|
8
8
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
9
9
|
import { appendFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
@@ -473,7 +473,7 @@ function applet(sourceDefinition, options = {}) {
|
|
|
473
473
|
const routesDirectory = resolve(resolvedRoot, "src/client/routes");
|
|
474
474
|
const hasClient = existsSync(routesDirectory);
|
|
475
475
|
const hasServer = existsSync(resolve(resolvedRoot, "src/server/main.ts"));
|
|
476
|
-
if (!hasServer && parsedDefinition.data.telephony?.events
|
|
476
|
+
if (!hasServer && parsedDefinition.data.telephony?.events?.length) throw new Error("An applet with telephony events must have src/server/main.ts.");
|
|
477
477
|
if (!hasServer && (parsedDefinition.data.schedules?.length ?? 0) > 0) throw new Error("An applet with schedules must have src/server/main.ts.");
|
|
478
478
|
const legacyMain = resolve(resolvedRoot, "src/client/main.tsx");
|
|
479
479
|
const indexHtml = resolve(resolvedRoot, "index.html");
|
|
@@ -504,6 +504,11 @@ function applet(sourceDefinition, options = {}) {
|
|
|
504
504
|
org: resolveWorkspaceOrg(callerRoot),
|
|
505
505
|
...parsedDefinition.data
|
|
506
506
|
});
|
|
507
|
+
const clsxFromCn = {
|
|
508
|
+
name: "hitch-clsx",
|
|
509
|
+
resolveId: (id) => id === "clsx" ? "\0hitch:clsx" : void 0,
|
|
510
|
+
load: (id) => id === "\0hitch:clsx" ? "export { clsx, clsx as default } from \"cn\";" : void 0
|
|
511
|
+
};
|
|
507
512
|
const orchestrator = {
|
|
508
513
|
name: "hitch-applet",
|
|
509
514
|
enforce: "pre",
|
|
@@ -524,7 +529,7 @@ function applet(sourceDefinition, options = {}) {
|
|
|
524
529
|
clearedOutDir = true;
|
|
525
530
|
}
|
|
526
531
|
if (environment.command === "build") themeStylesheetUrl = `${organizationInstanceUrl(await resolveInstanceUrl(void 0, options.store), manifest.org)}/theme.css`;
|
|
527
|
-
if (environment.command === "build"
|
|
532
|
+
if (environment.command === "build") {
|
|
528
533
|
contractSync ??= syncContract(resolve(root, ".hitch"), manifest, options);
|
|
529
534
|
await contractSync;
|
|
530
535
|
}
|
|
@@ -608,9 +613,10 @@ function applet(sourceDefinition, options = {}) {
|
|
|
608
613
|
base: `/${manifest.code}/`,
|
|
609
614
|
test: { include: ["test/**/*.test.{ts,tsx}"] },
|
|
610
615
|
resolve: { alias: {
|
|
611
|
-
clsx: "cn",
|
|
616
|
+
"clsx/lite": "cn/lite",
|
|
612
617
|
"tailwind-merge": "cn"
|
|
613
618
|
} },
|
|
619
|
+
optimizeDeps: { rolldownOptions: { plugins: [clsxFromCn] } },
|
|
614
620
|
...hasServer && { environments: { worker: { build: { rollupOptions: { output: { codeSplitting: false } } } } } }
|
|
615
621
|
};
|
|
616
622
|
},
|
|
@@ -801,9 +807,14 @@ function applet(sourceDefinition, options = {}) {
|
|
|
801
807
|
...plugin,
|
|
802
808
|
apply: ((_config, { mode }) => mode !== "test")
|
|
803
809
|
}));
|
|
810
|
+
const hasClientSource = existsSync(resolve("src/client/routes"));
|
|
804
811
|
return [
|
|
805
812
|
orchestrator,
|
|
806
|
-
|
|
813
|
+
{
|
|
814
|
+
...clsxFromCn,
|
|
815
|
+
enforce: "pre"
|
|
816
|
+
},
|
|
817
|
+
...hasClientSource ? [tanstackRouter({
|
|
807
818
|
generatedRouteTree: ".hitch/routeTree.gen.ts",
|
|
808
819
|
routesDirectory: "src/client/routes",
|
|
809
820
|
routeFileIgnorePattern: "__root",
|