@agent-native/core 0.70.3 → 0.72.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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +51 -0
- package/corpus/core/docs/content/actions.md +23 -0
- package/corpus/core/docs/content/audit-log.md +111 -0
- package/corpus/core/docs/content/multi-app-workspace.md +2 -2
- package/corpus/core/docs/design/durable-agent-runs.md +458 -4
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/action.ts +80 -1
- package/corpus/core/src/agent/durable-background.ts +192 -0
- package/corpus/core/src/agent/production-agent.ts +357 -13
- package/corpus/core/src/agent/run-manager.ts +66 -3
- package/corpus/core/src/agent/run-store.ts +143 -23
- package/corpus/core/src/agent/types.ts +20 -0
- package/corpus/core/src/application-state/store.ts +5 -0
- package/corpus/core/src/audit/actions/get-audit-event.ts +23 -0
- package/corpus/core/src/audit/actions/list-audit-events.ts +65 -0
- package/corpus/core/src/audit/cleanup-job.ts +100 -0
- package/corpus/core/src/audit/config.ts +91 -0
- package/corpus/core/src/audit/index.ts +43 -0
- package/corpus/core/src/audit/record.ts +143 -0
- package/corpus/core/src/audit/redact.ts +109 -0
- package/corpus/core/src/audit/store.ts +244 -0
- package/corpus/core/src/audit/types.ts +125 -0
- package/corpus/core/src/chat-threads/store.ts +10 -0
- package/corpus/core/src/client/AgentPanel.tsx +12 -9
- package/corpus/core/src/client/AssistantChat.tsx +6 -0
- package/corpus/core/src/client/blocks/library/FileTreeBlock.tsx +72 -14
- package/corpus/core/src/client/chat/repo-helpers.ts +38 -0
- package/corpus/core/src/db/client.ts +4 -0
- package/corpus/core/src/db/widen-columns.ts +75 -0
- package/corpus/core/src/deploy/build.ts +96 -0
- package/corpus/core/src/deploy/workspace-deploy.ts +121 -0
- package/corpus/core/src/mcp/build-server.ts +23 -13
- package/corpus/core/src/oauth-tokens/store.ts +6 -0
- package/corpus/core/src/provider-api/custom-registry.ts +8 -0
- package/corpus/core/src/resources/store.ts +10 -0
- package/corpus/core/src/scripts/runner.ts +6 -3
- package/corpus/core/src/server/action-discovery.ts +6 -0
- package/corpus/core/src/server/action-routes.ts +1 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +133 -66
- package/corpus/core/src/server/auth.ts +5 -0
- package/corpus/core/src/server/core-routes-plugin.ts +14 -0
- package/corpus/core/src/server/security-headers.ts +9 -6
- package/corpus/core/src/settings/store.ts +6 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +93 -0
- package/corpus/core/src/usage/store.ts +6 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +19 -0
- package/corpus/templates/analytics/AGENTS.md +8 -0
- package/corpus/templates/analytics/actions/compose-dashboard.ts +317 -0
- package/corpus/templates/analytics/changelog/2026-06-23-build-large-first-party-analytics-dashboards-in-one-fast-cal.md +6 -0
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +574 -0
- package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +60 -6
- package/corpus/templates/calendar/changelog/2026-06-23-added-a-heads-up-explaining-google-s-app-not-verified-screen.md +6 -0
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +20 -43
- package/corpus/templates/clips/app/components/recorder/recording-toolbar.tsx +5 -1
- package/corpus/templates/clips/app/lib/countdown-audio-cue.ts +3 -51
- package/corpus/templates/clips/changelog/2026-06-23-dragging-the-desktop-camera-bubble-now-glides-to-a-stop-at-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-public-clips-now-play-inline-in-slack-connect-a-workspac.md +6 -0
- package/corpus/templates/clips/chrome-extension/PERMISSIONS.md +72 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -10
- package/corpus/templates/clips/chrome-extension/src/background.ts +163 -32
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +301 -24
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +245 -67
- package/corpus/templates/clips/chrome-extension/src/overlay.css +77 -43
- package/corpus/templates/clips/chrome-extension/src/overlay.html +7 -1
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +102 -38
- package/corpus/templates/clips/chrome-extension/src/popup.html +62 -5
- package/corpus/templates/clips/chrome-extension/src/popup.ts +275 -4
- package/corpus/templates/clips/chrome-extension/src/styles.css +34 -0
- package/corpus/templates/clips/chrome-extension/vite.config.ts +5 -0
- package/corpus/templates/clips/desktop/src/lib/audio-cue.ts +21 -16
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +10 -17
- package/corpus/templates/clips/desktop/src/overlays/bubble.tsx +73 -23
- package/corpus/templates/clips/desktop/src/overlays/countdown.tsx +0 -6
- package/corpus/templates/clips/desktop/src/overlays/toolbar.tsx +56 -46
- package/corpus/templates/clips/desktop/src/styles.css +33 -5
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +113 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +3 -0
- package/corpus/templates/clips/server/lib/media-permissions.ts +5 -1
- package/corpus/templates/clips/shared/recording-audio.ts +62 -0
- package/corpus/templates/clips/shared/recording-core.ts +94 -0
- package/dist/action.d.ts +31 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +45 -1
- package/dist/action.js.map +1 -1
- package/dist/agent/context-xray/schema.d.ts +1 -1
- package/dist/agent/durable-background.d.ts +60 -0
- package/dist/agent/durable-background.d.ts.map +1 -0
- package/dist/agent/durable-background.js +144 -0
- package/dist/agent/durable-background.js.map +1 -0
- package/dist/agent/observational-memory/schema.d.ts +1 -1
- package/dist/agent/production-agent.d.ts +20 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +320 -14
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +48 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +45 -3
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +30 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +138 -24
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/types.d.ts +20 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/application-state/store.d.ts.map +1 -1
- package/dist/application-state/store.js +5 -0
- package/dist/application-state/store.js.map +1 -1
- package/dist/audit/actions/get-audit-event.d.ts +11 -0
- package/dist/audit/actions/get-audit-event.d.ts.map +1 -0
- package/dist/audit/actions/get-audit-event.js +22 -0
- package/dist/audit/actions/get-audit-event.js.map +1 -0
- package/dist/audit/actions/list-audit-events.d.ts +22 -0
- package/dist/audit/actions/list-audit-events.d.ts.map +1 -0
- package/dist/audit/actions/list-audit-events.js +61 -0
- package/dist/audit/actions/list-audit-events.js.map +1 -0
- package/dist/audit/cleanup-job.d.ts +12 -0
- package/dist/audit/cleanup-job.d.ts.map +1 -0
- package/dist/audit/cleanup-job.js +93 -0
- package/dist/audit/cleanup-job.js.map +1 -0
- package/dist/audit/config.d.ts +30 -0
- package/dist/audit/config.d.ts.map +1 -0
- package/dist/audit/config.js +65 -0
- package/dist/audit/config.js.map +1 -0
- package/dist/audit/index.d.ts +13 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +6 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/record.d.ts +24 -0
- package/dist/audit/record.d.ts.map +1 -0
- package/dist/audit/record.js +106 -0
- package/dist/audit/record.js.map +1 -0
- package/dist/audit/redact.d.ts +28 -0
- package/dist/audit/redact.d.ts.map +1 -0
- package/dist/audit/redact.js +109 -0
- package/dist/audit/redact.js.map +1 -0
- package/dist/audit/store.d.ts +14 -0
- package/dist/audit/store.d.ts.map +1 -0
- package/dist/audit/store.js +219 -0
- package/dist/audit/store.js.map +1 -0
- package/dist/audit/types.d.ts +114 -0
- package/dist/audit/types.d.ts.map +1 -0
- package/dist/audit/types.js +15 -0
- package/dist/audit/types.js.map +1 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +10 -0
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +2 -2
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +6 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/FileTreeBlock.js +37 -4
- package/dist/client/blocks/library/FileTreeBlock.js.map +1 -1
- package/dist/client/chat/repo-helpers.d.ts +16 -0
- package/dist/client/chat/repo-helpers.d.ts.map +1 -1
- package/dist/client/chat/repo-helpers.js +40 -0
- package/dist/client/chat/repo-helpers.js.map +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +3 -0
- package/dist/db/client.js.map +1 -1
- package/dist/db/widen-columns.d.ts +39 -0
- package/dist/db/widen-columns.d.ts.map +1 -0
- package/dist/db/widen-columns.js +73 -0
- package/dist/db/widen-columns.js.map +1 -0
- package/dist/deploy/build.d.ts +29 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +85 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/deploy/workspace-deploy.d.ts.map +1 -1
- package/dist/deploy/workspace-deploy.js +108 -0
- package/dist/deploy/workspace-deploy.js.map +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +24 -9
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/oauth-tokens/store.d.ts.map +1 -1
- package/dist/oauth-tokens/store.js +6 -0
- package/dist/oauth-tokens/store.js.map +1 -1
- package/dist/provider-api/custom-registry.d.ts.map +1 -1
- package/dist/provider-api/custom-registry.js +8 -0
- package/dist/provider-api/custom-registry.js.map +1 -1
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +9 -0
- package/dist/resources/store.js.map +1 -1
- package/dist/scripts/runner.js +4 -3
- package/dist/scripts/runner.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +6 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +1 -0
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +68 -13
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +5 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +13 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/security-headers.d.ts +8 -5
- package/dist/server/security-headers.d.ts.map +1 -1
- package/dist/server/security-headers.js +9 -6
- package/dist/server/security-headers.js.map +1 -1
- package/dist/settings/store.d.ts.map +1 -1
- package/dist/settings/store.js +6 -0
- package/dist/settings/store.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/audit-log/SKILL.md +93 -0
- package/dist/usage/store.d.ts.map +1 -1
- package/dist/usage/store.js +5 -0
- package/dist/usage/store.js.map +1 -1
- package/docs/content/actions.md +23 -0
- package/docs/content/audit-log.md +111 -0
- package/docs/content/multi-app-workspace.md +2 -2
- package/docs/design/durable-agent-runs.md +458 -4
- package/package.json +2 -1
- package/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +93 -0
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
// Loom-style in-page overlay host.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
1
|
+
// Loom-style in-page overlay host. The background service worker injects this
|
|
2
|
+
// into the LAUNCH TAB on demand via chrome.scripting.executeScript (covered by
|
|
3
|
+
// the activeTab permission the user grants when they click the extension), NOT
|
|
4
|
+
// declaratively on every page — that would need broad "<all_urls>" host access
|
|
5
|
+
// and Chrome's in-depth review. So the overlay lives on the tab the recording
|
|
6
|
+
// was started from; it does not follow across tabs unless CROSS_TAB_FOLLOW is
|
|
7
|
+
// re-enabled in background.ts (see PERMISSIONS.md). Wrapped in an IIFE so it
|
|
8
|
+
// emits a single self-contained classic script with no module imports/exports
|
|
9
|
+
// and leaks no names into the shared global scope. Its only job is to
|
|
10
|
+
// mount/unmount the overlay iframes; all UI and control logic lives inside the
|
|
11
|
+
// extension-origin overlay pages (src/overlay.html). The worker is the source of
|
|
12
|
+
// truth for which "parts" are visible and pushes them here.
|
|
8
13
|
|
|
9
14
|
(function clipsOverlayHost() {
|
|
10
15
|
type OverlayPart = "bubble" | "countdown" | "toolbar" | "saving";
|
|
@@ -13,6 +18,128 @@
|
|
|
13
18
|
const ALL_PARTS: OverlayPart[] = ["bubble", "countdown", "toolbar", "saving"];
|
|
14
19
|
const flags = window as unknown as { __clipsOverlayHostReady?: boolean };
|
|
15
20
|
|
|
21
|
+
// ----- Draggable, resizable camera bubble ---------------------------------
|
|
22
|
+
// Size + position persist in storage so the bubble stays where the user put it
|
|
23
|
+
// across pages and recordings (like the desktop app). The iframe can't move or
|
|
24
|
+
// resize itself, so the content script owns its geometry.
|
|
25
|
+
const BUBBLE_SIZES: Record<string, number> = { sm: 184, lg: 280 };
|
|
26
|
+
const bubbleGeom: { size: string; left: number | null; top: number | null } =
|
|
27
|
+
{ size: "lg", left: null, top: null };
|
|
28
|
+
let bubbleDragLayer: HTMLDivElement | null = null;
|
|
29
|
+
let bubblePersistTimer: ReturnType<typeof setTimeout> | undefined;
|
|
30
|
+
|
|
31
|
+
function bubbleSizePx(): number {
|
|
32
|
+
return BUBBLE_SIZES[bubbleGeom.size] ?? BUBBLE_SIZES.lg;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function clampBubble(
|
|
36
|
+
left: number,
|
|
37
|
+
top: number,
|
|
38
|
+
size: number,
|
|
39
|
+
): { left: number; top: number } {
|
|
40
|
+
return {
|
|
41
|
+
left: Math.max(8, Math.min(left, window.innerWidth - size - 8)),
|
|
42
|
+
top: Math.max(8, Math.min(top, window.innerHeight - size - 8)),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function applyBubbleGeom(): void {
|
|
47
|
+
const frame = document.getElementById(
|
|
48
|
+
partFrameId("bubble"),
|
|
49
|
+
) as HTMLIFrameElement | null;
|
|
50
|
+
if (!frame) return;
|
|
51
|
+
const size = bubbleSizePx();
|
|
52
|
+
const margin = 24;
|
|
53
|
+
const base = clampBubble(
|
|
54
|
+
bubbleGeom.left ?? margin,
|
|
55
|
+
bubbleGeom.top ?? window.innerHeight - size - margin,
|
|
56
|
+
size,
|
|
57
|
+
);
|
|
58
|
+
Object.assign(frame.style, {
|
|
59
|
+
left: `${base.left}px`,
|
|
60
|
+
top: `${base.top}px`,
|
|
61
|
+
bottom: "auto",
|
|
62
|
+
width: `${size}px`,
|
|
63
|
+
height: `${size}px`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function persistBubbleGeom(): void {
|
|
68
|
+
clearTimeout(bubblePersistTimer);
|
|
69
|
+
bubblePersistTimer = setTimeout(() => {
|
|
70
|
+
try {
|
|
71
|
+
chrome.storage.local.set({ bubbleGeom });
|
|
72
|
+
} catch {
|
|
73
|
+
/* ignore */
|
|
74
|
+
}
|
|
75
|
+
}, 200);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function startBubbleDrag(): void {
|
|
79
|
+
if (bubbleDragLayer) return;
|
|
80
|
+
const frame = document.getElementById(
|
|
81
|
+
partFrameId("bubble"),
|
|
82
|
+
) as HTMLIFrameElement | null;
|
|
83
|
+
if (!frame) return;
|
|
84
|
+
// Full-screen capture layer so the pointer keeps tracking after it leaves
|
|
85
|
+
// the small bubble iframe.
|
|
86
|
+
const layer = document.createElement("div");
|
|
87
|
+
Object.assign(layer.style, {
|
|
88
|
+
position: "fixed",
|
|
89
|
+
inset: "0",
|
|
90
|
+
zIndex: "2147483647",
|
|
91
|
+
cursor: "grabbing",
|
|
92
|
+
});
|
|
93
|
+
(document.documentElement || document.body).appendChild(layer);
|
|
94
|
+
bubbleDragLayer = layer;
|
|
95
|
+
const onMove = (e: PointerEvent): void => {
|
|
96
|
+
const size = bubbleSizePx();
|
|
97
|
+
const rect = frame.getBoundingClientRect();
|
|
98
|
+
const next = clampBubble(
|
|
99
|
+
rect.left + e.movementX,
|
|
100
|
+
rect.top + e.movementY,
|
|
101
|
+
size,
|
|
102
|
+
);
|
|
103
|
+
bubbleGeom.left = next.left;
|
|
104
|
+
bubbleGeom.top = next.top;
|
|
105
|
+
Object.assign(frame.style, {
|
|
106
|
+
left: `${next.left}px`,
|
|
107
|
+
top: `${next.top}px`,
|
|
108
|
+
bottom: "auto",
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
const onUp = (): void => {
|
|
112
|
+
layer.removeEventListener("pointermove", onMove);
|
|
113
|
+
layer.removeEventListener("pointerup", onUp);
|
|
114
|
+
layer.removeEventListener("pointercancel", onUp);
|
|
115
|
+
layer.remove();
|
|
116
|
+
bubbleDragLayer = null;
|
|
117
|
+
persistBubbleGeom();
|
|
118
|
+
};
|
|
119
|
+
layer.addEventListener("pointermove", onMove);
|
|
120
|
+
layer.addEventListener("pointerup", onUp);
|
|
121
|
+
layer.addEventListener("pointercancel", onUp);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
chrome.storage.local.get("bubbleGeom", (value) => {
|
|
126
|
+
if (chrome.runtime.lastError) return;
|
|
127
|
+
const g = value.bubbleGeom as
|
|
128
|
+
| { size?: unknown; left?: unknown; top?: unknown }
|
|
129
|
+
| undefined;
|
|
130
|
+
if (g && typeof g === "object") {
|
|
131
|
+
bubbleGeom.size = g.size === "sm" ? "sm" : "lg";
|
|
132
|
+
bubbleGeom.left = typeof g.left === "number" ? g.left : null;
|
|
133
|
+
bubbleGeom.top = typeof g.top === "number" ? g.top : null;
|
|
134
|
+
}
|
|
135
|
+
applyBubbleGeom();
|
|
136
|
+
});
|
|
137
|
+
} catch {
|
|
138
|
+
/* ignore */
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
window.addEventListener("resize", () => applyBubbleGeom());
|
|
142
|
+
|
|
16
143
|
function requestState(): void {
|
|
17
144
|
try {
|
|
18
145
|
chrome.runtime.sendMessage(
|
|
@@ -72,40 +199,62 @@
|
|
|
72
199
|
position: "absolute",
|
|
73
200
|
border: "none",
|
|
74
201
|
background: "transparent",
|
|
75
|
-
colorScheme: "
|
|
202
|
+
colorScheme: "dark",
|
|
76
203
|
pointerEvents: "auto",
|
|
77
204
|
});
|
|
78
205
|
frame.setAttribute("allowtransparency", "true");
|
|
79
206
|
if (part === "bubble") {
|
|
80
207
|
frame.allow = "camera; microphone";
|
|
208
|
+
// Above the countdown so the face stays sharp over the dim/blur. Exact
|
|
209
|
+
// size/position are set by applyBubbleGeom() once mounted.
|
|
210
|
+
const size = bubbleSizePx();
|
|
81
211
|
Object.assign(frame.style, {
|
|
82
212
|
left: "24px",
|
|
83
213
|
bottom: "24px",
|
|
84
|
-
width:
|
|
85
|
-
height:
|
|
214
|
+
width: `${size}px`,
|
|
215
|
+
height: `${size}px`,
|
|
216
|
+
// Clip the IFRAME itself to a circle so the iframe's opaque canvas (which
|
|
217
|
+
// a declared color-scheme always paints, dark or white) can never show as
|
|
218
|
+
// a square box around the bubble.
|
|
219
|
+
borderRadius: "50%",
|
|
220
|
+
overflow: "hidden",
|
|
221
|
+
zIndex: "3",
|
|
86
222
|
});
|
|
87
223
|
} else if (part === "toolbar") {
|
|
88
224
|
// Left-edge vertical pill (desktop layout). Height grows on hover via the
|
|
89
|
-
// resize message below.
|
|
225
|
+
// resize message below. Clipped to the pill's radius (the pill fills the
|
|
226
|
+
// iframe) so the opaque canvas can't show as a box; shadow on the iframe so
|
|
227
|
+
// the clip doesn't cut it.
|
|
90
228
|
Object.assign(frame.style, {
|
|
91
229
|
left: "16px",
|
|
92
|
-
top: "50%",
|
|
93
|
-
transform: "translateY(-50%)",
|
|
230
|
+
top: "calc(50% - 77px)",
|
|
94
231
|
width: "68px",
|
|
95
232
|
height: "154px",
|
|
233
|
+
borderRadius: "20px",
|
|
234
|
+
overflow: "hidden",
|
|
235
|
+
boxShadow: "0 10px 28px rgba(9, 9, 11, 0.45)",
|
|
236
|
+
zIndex: "2",
|
|
96
237
|
});
|
|
97
238
|
} else if (part === "saving") {
|
|
239
|
+
// Compact card: caption + a single indeterminate bar (no circular spinner).
|
|
240
|
+
// Clipped to the card radius (card fills the iframe) so no canvas box shows.
|
|
98
241
|
Object.assign(frame.style, {
|
|
99
242
|
left: "24px",
|
|
100
243
|
bottom: "24px",
|
|
101
|
-
width: "
|
|
102
|
-
height: "
|
|
244
|
+
width: "240px",
|
|
245
|
+
height: "64px",
|
|
246
|
+
borderRadius: "14px",
|
|
247
|
+
overflow: "hidden",
|
|
248
|
+
boxShadow: "0 10px 28px rgba(9, 9, 11, 0.45)",
|
|
249
|
+
zIndex: "2",
|
|
103
250
|
});
|
|
104
251
|
} else {
|
|
252
|
+
// countdown — full-screen dim/blur, below the bubble.
|
|
105
253
|
Object.assign(frame.style, {
|
|
106
254
|
inset: "0",
|
|
107
255
|
width: "100%",
|
|
108
256
|
height: "100%",
|
|
257
|
+
zIndex: "1",
|
|
109
258
|
});
|
|
110
259
|
}
|
|
111
260
|
}
|
|
@@ -114,29 +263,134 @@
|
|
|
114
263
|
if (document.getElementById(partFrameId(part))) return;
|
|
115
264
|
const frame = document.createElement("iframe");
|
|
116
265
|
frame.id = partFrameId(part);
|
|
266
|
+
if (part === "bubble") frame.allow = "camera; microphone";
|
|
117
267
|
const url = new URL(chrome.runtime.getURL("src/overlay.html"));
|
|
118
268
|
url.searchParams.set("part", part);
|
|
119
269
|
if (part === "countdown") url.searchParams.set("seconds", "3");
|
|
120
270
|
frame.src = url.toString();
|
|
121
271
|
styleFrame(frame, part);
|
|
122
272
|
container.appendChild(frame);
|
|
273
|
+
if (part === "bubble") applyBubbleGeom();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Camera-ready gating + connecting spinner: while the camera connects we show a
|
|
277
|
+
// simple centered spinner and keep the bubble hidden, then reveal the bubble and
|
|
278
|
+
// start the countdown once the feed is live — so the "3" never hangs and there's
|
|
279
|
+
// no half-loaded, un-draggable bubble during the wait. The bubble posts
|
|
280
|
+
// "camera-ready" when its video plays (or fails); a fallback timer proceeds
|
|
281
|
+
// anyway if the camera never connects.
|
|
282
|
+
const CONNECTING_ID = `${CONTAINER_ID}-connecting`;
|
|
283
|
+
let cameraReady = false;
|
|
284
|
+
let countdownDeferred = false;
|
|
285
|
+
let countdownFallbackTimer: ReturnType<typeof setTimeout> | undefined;
|
|
286
|
+
|
|
287
|
+
function showConnecting(container: HTMLDivElement): void {
|
|
288
|
+
if (document.getElementById(CONNECTING_ID)) return;
|
|
289
|
+
const wrap = document.createElement("div");
|
|
290
|
+
wrap.id = CONNECTING_ID;
|
|
291
|
+
Object.assign(wrap.style, {
|
|
292
|
+
position: "absolute",
|
|
293
|
+
inset: "0",
|
|
294
|
+
display: "flex",
|
|
295
|
+
alignItems: "center",
|
|
296
|
+
justifyContent: "center",
|
|
297
|
+
pointerEvents: "none",
|
|
298
|
+
zIndex: "4",
|
|
299
|
+
});
|
|
300
|
+
const chip = document.createElement("div");
|
|
301
|
+
Object.assign(chip.style, {
|
|
302
|
+
display: "flex",
|
|
303
|
+
alignItems: "center",
|
|
304
|
+
justifyContent: "center",
|
|
305
|
+
width: "84px",
|
|
306
|
+
height: "84px",
|
|
307
|
+
borderRadius: "20px",
|
|
308
|
+
background: "rgba(24, 24, 27, 0.92)",
|
|
309
|
+
boxShadow: "0 10px 28px rgba(9, 9, 11, 0.45)",
|
|
310
|
+
});
|
|
311
|
+
const spinner = document.createElement("div");
|
|
312
|
+
Object.assign(spinner.style, {
|
|
313
|
+
width: "40px",
|
|
314
|
+
height: "40px",
|
|
315
|
+
borderRadius: "50%",
|
|
316
|
+
border: "4px solid rgba(255, 255, 255, 0.18)",
|
|
317
|
+
borderTopColor: "rgba(255, 255, 255, 0.92)",
|
|
318
|
+
});
|
|
319
|
+
chip.appendChild(spinner);
|
|
320
|
+
wrap.appendChild(chip);
|
|
321
|
+
container.appendChild(wrap);
|
|
322
|
+
try {
|
|
323
|
+
spinner.animate(
|
|
324
|
+
[{ transform: "rotate(0deg)" }, { transform: "rotate(360deg)" }],
|
|
325
|
+
{ duration: 800, iterations: Infinity },
|
|
326
|
+
);
|
|
327
|
+
} catch {
|
|
328
|
+
/* Web Animations unavailable — a static ring is fine */
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function hideConnecting(): void {
|
|
333
|
+
document.getElementById(CONNECTING_ID)?.remove();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function setBubbleHidden(hidden: boolean): void {
|
|
337
|
+
const frame = document.getElementById(partFrameId("bubble"));
|
|
338
|
+
if (frame) (frame as HTMLElement).style.visibility = hidden ? "hidden" : "";
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function mountDeferredCountdown(): void {
|
|
342
|
+
countdownDeferred = false;
|
|
343
|
+
clearTimeout(countdownFallbackTimer);
|
|
344
|
+
hideConnecting();
|
|
345
|
+
setBubbleHidden(false);
|
|
346
|
+
const container = document.getElementById(
|
|
347
|
+
CONTAINER_ID,
|
|
348
|
+
) as HTMLDivElement | null;
|
|
349
|
+
if (container && !document.getElementById(partFrameId("countdown"))) {
|
|
350
|
+
mountPart(container, "countdown");
|
|
351
|
+
}
|
|
123
352
|
}
|
|
124
353
|
|
|
125
354
|
function reconcile(parts: OverlayPart[]): void {
|
|
355
|
+
console.log("[clips-cs] reconcile parts:", parts, "on", location.href);
|
|
126
356
|
const wanted = new Set(parts.filter((p) => ALL_PARTS.includes(p)));
|
|
357
|
+
// Leaving the countdown phase (recording / saving / idle): cancel any pending
|
|
358
|
+
// deferred countdown + spinner so a late fallback timer can't pop the "3-2-1"
|
|
359
|
+
// back up over a later overlay (this was the "countdown over the saving card"
|
|
360
|
+
// bug).
|
|
361
|
+
if (!wanted.has("countdown")) {
|
|
362
|
+
countdownDeferred = false;
|
|
363
|
+
clearTimeout(countdownFallbackTimer);
|
|
364
|
+
hideConnecting();
|
|
365
|
+
}
|
|
127
366
|
if (wanted.size === 0) {
|
|
128
367
|
document.getElementById(CONTAINER_ID)?.remove();
|
|
368
|
+
cameraReady = false;
|
|
129
369
|
return;
|
|
130
370
|
}
|
|
131
371
|
const container = ensureContainer();
|
|
372
|
+
const gateCountdown =
|
|
373
|
+
wanted.has("countdown") && wanted.has("bubble") && !cameraReady;
|
|
132
374
|
for (const part of ALL_PARTS) {
|
|
133
375
|
const existing = document.getElementById(partFrameId(part));
|
|
134
376
|
if (wanted.has(part)) {
|
|
377
|
+
// Hold the countdown (showing the spinner) until the camera feed is live.
|
|
378
|
+
if (part === "countdown" && gateCountdown) {
|
|
379
|
+
if (!existing && !countdownDeferred) {
|
|
380
|
+
countdownDeferred = true;
|
|
381
|
+
clearTimeout(countdownFallbackTimer);
|
|
382
|
+
countdownFallbackTimer = setTimeout(mountDeferredCountdown, 12000);
|
|
383
|
+
}
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
135
386
|
if (!existing) mountPart(container, part);
|
|
387
|
+
// Keep the bubble hidden behind the spinner until the feed is live.
|
|
388
|
+
if (part === "bubble" && gateCountdown) setBubbleHidden(true);
|
|
136
389
|
} else if (existing) {
|
|
137
390
|
existing.remove();
|
|
138
391
|
}
|
|
139
392
|
}
|
|
393
|
+
if (gateCountdown) showConnecting(container);
|
|
140
394
|
}
|
|
141
395
|
|
|
142
396
|
// Guard against rare double-injection (SPA soft-reloads re-running the script).
|
|
@@ -157,20 +411,43 @@
|
|
|
157
411
|
}
|
|
158
412
|
});
|
|
159
413
|
|
|
160
|
-
//
|
|
161
|
-
//
|
|
414
|
+
// Overlay iframes post layout requests (toolbar hover-resize, bubble drag and
|
|
415
|
+
// size). Only trust messages from our own extension-origin frames.
|
|
162
416
|
window.addEventListener("message", (event) => {
|
|
163
417
|
const data = event.data as
|
|
164
|
-
| {
|
|
418
|
+
| {
|
|
419
|
+
source?: string;
|
|
420
|
+
kind?: string;
|
|
421
|
+
part?: string;
|
|
422
|
+
height?: number;
|
|
423
|
+
size?: string;
|
|
424
|
+
}
|
|
165
425
|
| undefined;
|
|
166
|
-
if (!data || data.source !== "clips-overlay"
|
|
426
|
+
if (!data || data.source !== "clips-overlay") return;
|
|
427
|
+
if (event.origin !== chrome.runtime.getURL("").replace(/\/$/, "")) return;
|
|
428
|
+
|
|
429
|
+
if (data.kind === "resize" && data.part === "toolbar") {
|
|
430
|
+
const frame = document.getElementById(partFrameId("toolbar"));
|
|
431
|
+
if (frame && typeof data.height === "number") {
|
|
432
|
+
frame.style.height = `${Math.round(data.height)}px`;
|
|
433
|
+
}
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
if (data.kind === "camera-ready") {
|
|
437
|
+
cameraReady = true;
|
|
438
|
+
if (countdownDeferred) mountDeferredCountdown();
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
if (data.kind === "bubble-drag-start") {
|
|
442
|
+
startBubbleDrag();
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (data.kind === "bubble-size") {
|
|
446
|
+
bubbleGeom.size = data.size === "sm" ? "sm" : "lg";
|
|
447
|
+
applyBubbleGeom();
|
|
448
|
+
persistBubbleGeom();
|
|
167
449
|
return;
|
|
168
450
|
}
|
|
169
|
-
if (event.origin !== chrome.runtime.getURL("").replace(/\/$/, "")) return;
|
|
170
|
-
const part = data.part === "toolbar" ? "toolbar" : null;
|
|
171
|
-
if (!part || typeof data.height !== "number") return;
|
|
172
|
-
const frame = document.getElementById(partFrameId(part));
|
|
173
|
-
if (frame) frame.style.height = `${Math.round(data.height)}px`;
|
|
174
451
|
});
|
|
175
452
|
|
|
176
453
|
syncIfRecording();
|