@callembed/sdk 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +66 -2
- package/dist/callembed.iife.js +129 -0
- package/dist/core/agent.d.ts +10 -0
- package/dist/core/agent.js +19 -0
- package/dist/core/api.d.ts +44 -0
- package/dist/core/api.js +169 -0
- package/dist/core/audio.d.ts +20 -0
- package/dist/core/audio.js +68 -0
- package/dist/core/call.d.ts +45 -0
- package/dist/core/call.js +252 -0
- package/dist/core/client.d.ts +53 -0
- package/dist/core/client.js +157 -0
- package/dist/core/errors.d.ts +22 -0
- package/dist/core/errors.js +41 -0
- package/dist/core/events.d.ts +20 -0
- package/dist/core/events.js +43 -0
- package/dist/core/metadata.d.ts +13 -0
- package/dist/core/metadata.js +38 -0
- package/dist/core/ring-watch.d.ts +20 -0
- package/dist/core/ring-watch.js +46 -0
- package/dist/core/room-events.d.ts +13 -0
- package/dist/core/room-events.js +29 -0
- package/dist/core/state-machine.d.ts +11 -0
- package/dist/core/state-machine.js +44 -0
- package/dist/core/teardown.d.ts +49 -0
- package/dist/core/teardown.js +107 -0
- package/dist/core/types.d.ts +175 -0
- package/dist/core/types.js +5 -0
- package/dist/iife.d.ts +23 -0
- package/dist/iife.js +27 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +10 -0
- package/dist/ui/bind.d.ts +14 -0
- package/dist/ui/bind.js +82 -0
- package/dist/ui/button.d.ts +28 -0
- package/dist/ui/button.js +57 -0
- package/dist/ui/copy-en.d.ts +3 -0
- package/dist/ui/copy-en.js +25 -0
- package/dist/ui/copy-zh-tw.d.ts +28 -0
- package/dist/ui/copy-zh-tw.js +25 -0
- package/dist/ui/copy.d.ts +18 -0
- package/dist/ui/copy.js +45 -0
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/index.js +12 -0
- package/dist/ui/panel.d.ts +26 -0
- package/dist/ui/panel.js +62 -0
- package/dist/ui/theme.d.ts +13 -0
- package/dist/ui/theme.js +124 -0
- package/package.json +68 -4
package/dist/ui/panel.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ensureStyles } from "./theme.js";
|
|
2
|
+
function el(tag, className, text) {
|
|
3
|
+
const node = document.createElement(tag);
|
|
4
|
+
if (className)
|
|
5
|
+
node.className = className;
|
|
6
|
+
if (text !== undefined)
|
|
7
|
+
node.textContent = text;
|
|
8
|
+
return node;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Build the default call panel with `createElement` / `textContent` only.
|
|
12
|
+
* Never interprets copy strings as HTML.
|
|
13
|
+
*/
|
|
14
|
+
export function createCallPanel(options) {
|
|
15
|
+
ensureStyles();
|
|
16
|
+
const { copy, branding = true, container } = options;
|
|
17
|
+
const root = el("div", "ce-panel");
|
|
18
|
+
root.setAttribute("role", "dialog");
|
|
19
|
+
root.setAttribute("aria-label", copy.ariaLabel);
|
|
20
|
+
if (container)
|
|
21
|
+
root.dataset.embedded = "true";
|
|
22
|
+
const brandEl = el("p", "ce-brand", copy.brand);
|
|
23
|
+
if (!branding)
|
|
24
|
+
brandEl.hidden = true;
|
|
25
|
+
const titleEl = el("h2", "ce-title", copy.title);
|
|
26
|
+
const statusEl = el("p", "ce-status", copy.connecting);
|
|
27
|
+
statusEl.dataset.tone = "neutral";
|
|
28
|
+
const actions = el("div", "ce-actions");
|
|
29
|
+
const muteBtn = el("button", "ce-btn ce-btn-primary", copy.mute);
|
|
30
|
+
muteBtn.type = "button";
|
|
31
|
+
muteBtn.dataset.ce = "mute";
|
|
32
|
+
const hangupBtn = el("button", "ce-btn ce-btn-danger", copy.hangup);
|
|
33
|
+
hangupBtn.type = "button";
|
|
34
|
+
hangupBtn.dataset.ce = "hangup";
|
|
35
|
+
actions.append(muteBtn, hangupBtn);
|
|
36
|
+
const metaEl = el("p", "ce-meta", "");
|
|
37
|
+
metaEl.hidden = true;
|
|
38
|
+
metaEl.dataset.ce = "meta";
|
|
39
|
+
root.append(brandEl, titleEl, statusEl, actions, metaEl);
|
|
40
|
+
(container ?? document.body).appendChild(root);
|
|
41
|
+
return { root, brandEl, titleEl, statusEl, muteBtn, hangupBtn, metaEl };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Update panel status line (always via `textContent`).
|
|
45
|
+
*/
|
|
46
|
+
export function setPanelStatus(panel, status, copy, message) {
|
|
47
|
+
const labels = {
|
|
48
|
+
idle: copy.ready,
|
|
49
|
+
connecting: copy.connecting,
|
|
50
|
+
ringing: copy.ringing,
|
|
51
|
+
answered: copy.answered,
|
|
52
|
+
ended: copy.ended,
|
|
53
|
+
error: copy.error,
|
|
54
|
+
};
|
|
55
|
+
panel.statusEl.textContent = message || labels[status];
|
|
56
|
+
panel.statusEl.dataset.tone =
|
|
57
|
+
status === "error" ? "error" : status === "answered" ? "ok" : "neutral";
|
|
58
|
+
panel.root.dataset.status = status;
|
|
59
|
+
}
|
|
60
|
+
export function destroyPanel(panel) {
|
|
61
|
+
panel.root.remove();
|
|
62
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inject default CallEmbed UI styles once.
|
|
3
|
+
*
|
|
4
|
+
* Defaults use `var(--name, fallback)` so `:root { --ce-accent: … }` overrides
|
|
5
|
+
* work. Do not re-declare the same custom properties on `.ce-panel` as solid
|
|
6
|
+
* values that shadow inherited `:root` tokens.
|
|
7
|
+
*
|
|
8
|
+
* Variables: `--ce-bg` `--ce-fg` `--ce-muted` `--ce-accent` `--ce-danger`
|
|
9
|
+
* `--ce-ok` `--ce-error` `--ce-border` `--ce-radius` `--ce-font` `--ce-shadow`
|
|
10
|
+
*/
|
|
11
|
+
export declare function ensureStyles(): void;
|
|
12
|
+
/** Documented theme variable names for API reference / tests. */
|
|
13
|
+
export declare const THEME_VARIABLES: readonly ["--ce-bg", "--ce-fg", "--ce-muted", "--ce-accent", "--ce-danger", "--ce-ok", "--ce-error", "--ce-border", "--ce-radius", "--ce-font", "--ce-shadow"];
|
package/dist/ui/theme.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
const STYLE_ID = "callembed-sdk-styles";
|
|
2
|
+
/**
|
|
3
|
+
* Inject default CallEmbed UI styles once.
|
|
4
|
+
*
|
|
5
|
+
* Defaults use `var(--name, fallback)` so `:root { --ce-accent: … }` overrides
|
|
6
|
+
* work. Do not re-declare the same custom properties on `.ce-panel` as solid
|
|
7
|
+
* values that shadow inherited `:root` tokens.
|
|
8
|
+
*
|
|
9
|
+
* Variables: `--ce-bg` `--ce-fg` `--ce-muted` `--ce-accent` `--ce-danger`
|
|
10
|
+
* `--ce-ok` `--ce-error` `--ce-border` `--ce-radius` `--ce-font` `--ce-shadow`
|
|
11
|
+
*/
|
|
12
|
+
export function ensureStyles() {
|
|
13
|
+
if (typeof document === "undefined")
|
|
14
|
+
return;
|
|
15
|
+
if (document.getElementById(STYLE_ID))
|
|
16
|
+
return;
|
|
17
|
+
const style = document.createElement("style");
|
|
18
|
+
style.id = STYLE_ID;
|
|
19
|
+
style.textContent = `
|
|
20
|
+
:where(.ce-panel, .ce-launch) {
|
|
21
|
+
font-family: var(--ce-font, "Noto Sans TC", "IBM Plex Sans", "Segoe UI", sans-serif);
|
|
22
|
+
color: var(--ce-fg, #e8eef4);
|
|
23
|
+
}
|
|
24
|
+
.ce-panel {
|
|
25
|
+
position: fixed;
|
|
26
|
+
right: 1rem;
|
|
27
|
+
bottom: 1rem;
|
|
28
|
+
z-index: 2147483000;
|
|
29
|
+
width: min(320px, calc(100vw - 2rem));
|
|
30
|
+
background: var(--ce-bg, linear-gradient(160deg, #121820, #1a2430));
|
|
31
|
+
border: 1px solid var(--ce-border, #2a3a46);
|
|
32
|
+
border-radius: var(--ce-radius, 0);
|
|
33
|
+
box-shadow: var(--ce-shadow, 0 12px 40px rgba(0,0,0,.35));
|
|
34
|
+
padding: 1rem;
|
|
35
|
+
}
|
|
36
|
+
.ce-panel[data-embedded="true"] {
|
|
37
|
+
position: relative;
|
|
38
|
+
right: auto;
|
|
39
|
+
bottom: auto;
|
|
40
|
+
width: 100%;
|
|
41
|
+
box-shadow: none;
|
|
42
|
+
}
|
|
43
|
+
.ce-brand {
|
|
44
|
+
font-size: 0.7rem;
|
|
45
|
+
letter-spacing: 0.08em;
|
|
46
|
+
text-transform: uppercase;
|
|
47
|
+
color: var(--ce-muted, #9aadb8);
|
|
48
|
+
margin: 0 0 0.35rem;
|
|
49
|
+
}
|
|
50
|
+
.ce-title {
|
|
51
|
+
margin: 0 0 0.5rem;
|
|
52
|
+
font-size: 1.05rem;
|
|
53
|
+
font-weight: 600;
|
|
54
|
+
}
|
|
55
|
+
.ce-status {
|
|
56
|
+
margin: 0 0 0.85rem;
|
|
57
|
+
color: var(--ce-muted, #9aadb8);
|
|
58
|
+
font-size: 0.9rem;
|
|
59
|
+
line-height: 1.4;
|
|
60
|
+
min-height: 1.25em;
|
|
61
|
+
}
|
|
62
|
+
.ce-status[data-tone="error"] { color: var(--ce-error, #f0c4c4); }
|
|
63
|
+
.ce-status[data-tone="ok"] { color: var(--ce-ok, #9fd6c0); }
|
|
64
|
+
.ce-actions {
|
|
65
|
+
display: flex;
|
|
66
|
+
flex-wrap: wrap;
|
|
67
|
+
gap: 0.5rem;
|
|
68
|
+
}
|
|
69
|
+
.ce-btn {
|
|
70
|
+
appearance: none;
|
|
71
|
+
border: 1px solid var(--ce-border, #2a3a46);
|
|
72
|
+
border-radius: var(--ce-radius, 0);
|
|
73
|
+
background: transparent;
|
|
74
|
+
color: inherit;
|
|
75
|
+
padding: 0.55rem 0.85rem;
|
|
76
|
+
font: inherit;
|
|
77
|
+
cursor: pointer;
|
|
78
|
+
}
|
|
79
|
+
.ce-btn:hover { border-color: var(--ce-accent, #5eb0d8); }
|
|
80
|
+
.ce-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
81
|
+
.ce-btn-primary {
|
|
82
|
+
background: color-mix(in srgb, var(--ce-accent, #5eb0d8) 25%, transparent);
|
|
83
|
+
border-color: color-mix(in srgb, var(--ce-accent, #5eb0d8) 55%, var(--ce-border, #2a3a46));
|
|
84
|
+
}
|
|
85
|
+
.ce-btn-danger {
|
|
86
|
+
border-color: color-mix(in srgb, var(--ce-danger, #c44b4b) 55%, var(--ce-border, #2a3a46));
|
|
87
|
+
color: var(--ce-error, #f0c4c4);
|
|
88
|
+
}
|
|
89
|
+
.ce-meta {
|
|
90
|
+
margin: 0.75rem 0 0;
|
|
91
|
+
font-size: 0.75rem;
|
|
92
|
+
color: var(--ce-muted, #9aadb8);
|
|
93
|
+
word-break: break-all;
|
|
94
|
+
}
|
|
95
|
+
.ce-launch {
|
|
96
|
+
appearance: none;
|
|
97
|
+
display: inline-block;
|
|
98
|
+
border: 1px solid var(--ce-border, #2a3a46);
|
|
99
|
+
border-radius: var(--ce-radius, 0);
|
|
100
|
+
background: color-mix(in srgb, var(--ce-accent, #5eb0d8) 25%, transparent);
|
|
101
|
+
color: var(--ce-fg, #e8eef4);
|
|
102
|
+
padding: 0.7rem 1.1rem;
|
|
103
|
+
font-size: inherit;
|
|
104
|
+
cursor: pointer;
|
|
105
|
+
}
|
|
106
|
+
.ce-launch:hover { border-color: var(--ce-accent, #5eb0d8); }
|
|
107
|
+
.ce-launch:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
108
|
+
`;
|
|
109
|
+
document.head.appendChild(style);
|
|
110
|
+
}
|
|
111
|
+
/** Documented theme variable names for API reference / tests. */
|
|
112
|
+
export const THEME_VARIABLES = [
|
|
113
|
+
"--ce-bg",
|
|
114
|
+
"--ce-fg",
|
|
115
|
+
"--ce-muted",
|
|
116
|
+
"--ce-accent",
|
|
117
|
+
"--ce-danger",
|
|
118
|
+
"--ce-ok",
|
|
119
|
+
"--ce-error",
|
|
120
|
+
"--ce-border",
|
|
121
|
+
"--ce-radius",
|
|
122
|
+
"--ce-font",
|
|
123
|
+
"--ce-shadow",
|
|
124
|
+
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@callembed/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CallEmbed embeddable web SDK — headless core + optional UI",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Sealby",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./ui": {
|
|
18
|
+
"types": "./dist/ui/index.d.ts",
|
|
19
|
+
"import": "./dist/ui/index.js",
|
|
20
|
+
"default": "./dist/ui/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"sideEffects": [
|
|
25
|
+
"./dist/callembed.iife.js"
|
|
26
|
+
],
|
|
27
|
+
"unpkg": "./dist/callembed.iife.js",
|
|
28
|
+
"jsdelivr": "./dist/callembed.iife.js",
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc -p tsconfig.json && node ./scripts/build-iife.mjs",
|
|
36
|
+
"prepack": "npm run build",
|
|
37
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
38
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"test:watch": "vitest",
|
|
41
|
+
"pack:verify": "node ./scripts/pack-verify.mjs",
|
|
42
|
+
"pack:verify:workflow": "node ./scripts/pack-verify-workflow-smoke.mjs"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://callembed.sealby.me",
|
|
45
|
+
"keywords": [
|
|
46
|
+
"callembed",
|
|
47
|
+
"voice",
|
|
48
|
+
"webrtc",
|
|
49
|
+
"livekit",
|
|
50
|
+
"embed",
|
|
51
|
+
"sdk",
|
|
52
|
+
"realtime",
|
|
53
|
+
"customer-support"
|
|
54
|
+
],
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=20"
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"livekit-client": "^2.13.0"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"esbuild": "0.25.4",
|
|
66
|
+
"jsdom": "26.1.0",
|
|
67
|
+
"typescript": "^5.8.3",
|
|
68
|
+
"vitest": "3.2.7"
|
|
69
|
+
}
|
|
70
|
+
}
|