@callembed/sdk 0.0.0-stage → 0.1.2

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 (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -2
  3. package/dist/callembed.iife.js +129 -0
  4. package/dist/core/agent.d.ts +10 -0
  5. package/dist/core/agent.js +19 -0
  6. package/dist/core/api.d.ts +44 -0
  7. package/dist/core/api.js +169 -0
  8. package/dist/core/audio.d.ts +20 -0
  9. package/dist/core/audio.js +68 -0
  10. package/dist/core/call.d.ts +45 -0
  11. package/dist/core/call.js +252 -0
  12. package/dist/core/client.d.ts +53 -0
  13. package/dist/core/client.js +174 -0
  14. package/dist/core/errors.d.ts +22 -0
  15. package/dist/core/errors.js +41 -0
  16. package/dist/core/events.d.ts +20 -0
  17. package/dist/core/events.js +43 -0
  18. package/dist/core/hosted-embed.d.ts +14 -0
  19. package/dist/core/hosted-embed.js +14 -0
  20. package/dist/core/metadata.d.ts +13 -0
  21. package/dist/core/metadata.js +38 -0
  22. package/dist/core/ring-watch.d.ts +20 -0
  23. package/dist/core/ring-watch.js +46 -0
  24. package/dist/core/room-events.d.ts +13 -0
  25. package/dist/core/room-events.js +29 -0
  26. package/dist/core/state-machine.d.ts +11 -0
  27. package/dist/core/state-machine.js +44 -0
  28. package/dist/core/teardown.d.ts +49 -0
  29. package/dist/core/teardown.js +107 -0
  30. package/dist/core/types.d.ts +184 -0
  31. package/dist/core/types.js +5 -0
  32. package/dist/iife.d.ts +23 -0
  33. package/dist/iife.js +27 -0
  34. package/dist/index.d.ts +12 -0
  35. package/dist/index.js +11 -0
  36. package/dist/ui/bind.d.ts +14 -0
  37. package/dist/ui/bind.js +82 -0
  38. package/dist/ui/button.d.ts +28 -0
  39. package/dist/ui/button.js +57 -0
  40. package/dist/ui/copy-en.d.ts +3 -0
  41. package/dist/ui/copy-en.js +25 -0
  42. package/dist/ui/copy-zh-tw.d.ts +28 -0
  43. package/dist/ui/copy-zh-tw.js +25 -0
  44. package/dist/ui/copy.d.ts +18 -0
  45. package/dist/ui/copy.js +45 -0
  46. package/dist/ui/index.d.ts +12 -0
  47. package/dist/ui/index.js +12 -0
  48. package/dist/ui/panel.d.ts +26 -0
  49. package/dist/ui/panel.js +62 -0
  50. package/dist/ui/theme.d.ts +13 -0
  51. package/dist/ui/theme.js +124 -0
  52. package/package.json +76 -4
@@ -0,0 +1,45 @@
1
+ import { enCopy } from "./copy-en.js";
2
+ import { zhTwCopy } from "./copy-zh-tw.js";
3
+ const PACKS = {
4
+ "zh-TW": { ...zhTwCopy },
5
+ en: { ...enCopy },
6
+ };
7
+ /**
8
+ * Resolve panel copy from locale + safe string overrides.
9
+ * Override values are coerced to strings (never used as HTML).
10
+ */
11
+ export function resolveCopy(runtime) {
12
+ const locale = runtime?.locale ?? "en";
13
+ const base = { ...(PACKS[locale] ?? PACKS.en) };
14
+ const overrides = runtime?.copyOverrides ?? {};
15
+ for (const [key, value] of Object.entries(overrides)) {
16
+ if (key in base && value !== undefined && value !== null) {
17
+ base[key] = String(value);
18
+ }
19
+ }
20
+ return base;
21
+ }
22
+ /**
23
+ * Map machine error codes to friendly panel copy (never show raw codes to B).
24
+ */
25
+ /**
26
+ * Map machine error codes to friendly panel copy.
27
+ * Unknown codes use the locale generic `error` string — never raw server text.
28
+ */
29
+ export function friendlyErrorMessage(copy, code, _fallback) {
30
+ switch (code) {
31
+ case "no_agent_online":
32
+ return copy.errNoAgent;
33
+ case "quota_exceeded":
34
+ return copy.errQuota;
35
+ case "mic_permission_denied":
36
+ return copy.errMicDenied;
37
+ case "no_microphone":
38
+ return copy.errNoMic;
39
+ case "connection_failed":
40
+ return copy.errConnection;
41
+ default:
42
+ return copy.error;
43
+ }
44
+ }
45
+ export { zhTwCopy, enCopy };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Optional CallEmbed UI layer (panel, launch button, theme, copy).
3
+ * Depends on the headless core; core never imports this module.
4
+ *
5
+ * Importing this package does **not** change `CallEmbed.startCall` behaviour.
6
+ * Call {@link enableDefaultUi} or {@link mountButton} to attach the default panel.
7
+ */
8
+ export { enableDefaultUi, createPanelHooks } from "./bind.js";
9
+ export { mountButton, type MountButtonOptions, type MountButtonResult, } from "./button.js";
10
+ export { createCallPanel, destroyPanel, setPanelStatus, type CallPanelElements, type CreatePanelOptions, } from "./panel.js";
11
+ export { ensureStyles, THEME_VARIABLES } from "./theme.js";
12
+ export { resolveCopy, friendlyErrorMessage, zhTwCopy, enCopy, type PanelCopy, } from "./copy.js";
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Optional CallEmbed UI layer (panel, launch button, theme, copy).
3
+ * Depends on the headless core; core never imports this module.
4
+ *
5
+ * Importing this package does **not** change `CallEmbed.startCall` behaviour.
6
+ * Call {@link enableDefaultUi} or {@link mountButton} to attach the default panel.
7
+ */
8
+ export { enableDefaultUi, createPanelHooks } from "./bind.js";
9
+ export { mountButton, } from "./button.js";
10
+ export { createCallPanel, destroyPanel, setPanelStatus, } from "./panel.js";
11
+ export { ensureStyles, THEME_VARIABLES } from "./theme.js";
12
+ export { resolveCopy, friendlyErrorMessage, zhTwCopy, enCopy, } from "./copy.js";
@@ -0,0 +1,26 @@
1
+ import type { CallStatus } from "../core/types.js";
2
+ import type { PanelCopy } from "./copy-zh-tw.js";
3
+ export interface CallPanelElements {
4
+ root: HTMLElement;
5
+ brandEl: HTMLElement;
6
+ titleEl: HTMLElement;
7
+ statusEl: HTMLElement;
8
+ muteBtn: HTMLButtonElement;
9
+ hangupBtn: HTMLButtonElement;
10
+ metaEl: HTMLElement;
11
+ }
12
+ export interface CreatePanelOptions {
13
+ copy: PanelCopy;
14
+ branding?: boolean;
15
+ container?: HTMLElement;
16
+ }
17
+ /**
18
+ * Build the default call panel with `createElement` / `textContent` only.
19
+ * Never interprets copy strings as HTML.
20
+ */
21
+ export declare function createCallPanel(options: CreatePanelOptions): CallPanelElements;
22
+ /**
23
+ * Update panel status line (always via `textContent`).
24
+ */
25
+ export declare function setPanelStatus(panel: CallPanelElements, status: CallStatus, copy: PanelCopy, message?: string): void;
26
+ export declare function destroyPanel(panel: CallPanelElements): void;
@@ -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"];
@@ -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,78 @@
1
1
  {
2
2
  "name": "@callembed/sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.2",
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
+ "repository": {
46
+ "type": "git",
47
+ "url": "git+https://github.com/TonyFeng1203/CallEmbed.git",
48
+ "directory": "packages/sdk"
49
+ },
50
+ "bugs": {
51
+ "url": "https://github.com/TonyFeng1203/CallEmbed/issues"
52
+ },
53
+ "keywords": [
54
+ "callembed",
55
+ "voice",
56
+ "webrtc",
57
+ "livekit",
58
+ "embed",
59
+ "sdk",
60
+ "realtime",
61
+ "customer-support"
62
+ ],
63
+ "engines": {
64
+ "node": ">=20"
65
+ },
66
+ "publishConfig": {
67
+ "access": "public"
68
+ },
69
+ "dependencies": {
70
+ "livekit-client": "^2.13.0"
71
+ },
72
+ "devDependencies": {
73
+ "esbuild": "0.25.4",
74
+ "jsdom": "26.1.0",
75
+ "typescript": "^5.8.3",
76
+ "vitest": "3.2.7"
77
+ }
78
+ }