@devaccel-labs/app-comments-toolbar 1.0.0 → 1.3.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/README.md +152 -18
- package/dist/react.js +516 -61
- package/dist/server.js +342 -14
- package/dist/types/capture/api.d.ts +30 -0
- package/dist/types/capture/editor.d.ts +10 -0
- package/dist/types/capture/ignore.d.ts +19 -0
- package/dist/types/capture/index.d.ts +1 -0
- package/dist/types/capture/picker.d.ts +9 -0
- package/dist/types/capture/picture.d.ts +29 -0
- package/dist/types/capture/surface.d.ts +39 -0
- package/dist/types/core/index.d.ts +2 -2
- package/dist/types/core/resolve.d.ts +2 -1
- package/dist/types/core/types.d.ts +11 -0
- package/dist/types/debug-context.d.ts +7 -0
- package/dist/types/dictation.d.ts +9 -0
- package/dist/types/embed/controller.d.ts +61 -0
- package/dist/types/embed/index.d.ts +1 -0
- package/dist/types/embed/options.d.ts +35 -0
- package/dist/types/help/index.d.ts +1 -0
- package/dist/types/help/panel-index.d.ts +1 -0
- package/dist/types/help/panel-styles.d.ts +1 -0
- package/dist/types/help/panel.d.ts +4 -0
- package/dist/types/help/pill-styles.d.ts +11 -0
- package/dist/types/help/pill.d.ts +44 -0
- package/dist/types/help/react.d.ts +12 -0
- package/dist/types/help/requests.d.ts +2 -0
- package/dist/types/help/types.d.ts +119 -0
- package/dist/types/help/wire.d.ts +61 -0
- package/dist/types/native-wire.d.ts +63 -0
- package/dist/types/native.d.ts +43 -0
- package/dist/types/react.d.ts +8 -1
- package/dist/types/replay.d.ts +6 -0
- package/dist/types/runtime/api.d.ts +250 -0
- package/dist/types/runtime/attachment-model.d.ts +16 -0
- package/dist/types/runtime/attachments.d.ts +43 -7
- package/dist/types/runtime/boot.d.ts +39 -0
- package/dist/types/runtime/browser-label.d.ts +5 -0
- package/dist/types/runtime/build-id.d.ts +7 -0
- package/dist/types/runtime/capture-ui.d.ts +45 -21
- package/dist/types/runtime/clarity-model.d.ts +260 -0
- package/dist/types/runtime/clarity-ui.d.ts +62 -0
- package/dist/types/runtime/client.d.ts +6 -197
- package/dist/types/runtime/composer-size.d.ts +30 -0
- package/dist/types/runtime/dashboard.d.ts +17 -2
- package/dist/types/runtime/debug-context-core.d.ts +88 -0
- package/dist/types/runtime/debug-context-host.d.ts +39 -0
- package/dist/types/runtime/debug-context-model.d.ts +352 -0
- package/dist/types/runtime/debug-context-redact.d.ts +48 -0
- package/dist/types/runtime/debug-context.d.ts +36 -0
- package/dist/types/runtime/details-model.d.ts +7 -0
- package/dist/types/runtime/details.d.ts +7 -5
- package/dist/types/runtime/dictation-engine.d.ts +98 -0
- package/dist/types/runtime/dictation-model.d.ts +28 -0
- package/dist/types/runtime/dictation-ui.d.ts +56 -0
- package/dist/types/runtime/dictation.d.ts +66 -0
- package/dist/types/runtime/draft-dock.d.ts +32 -0
- package/dist/types/runtime/draft-model.d.ts +104 -0
- package/dist/types/runtime/draft-store.d.ts +15 -0
- package/dist/types/runtime/edits.d.ts +1 -1
- package/dist/types/runtime/font.d.ts +16 -0
- package/dist/types/runtime/format.d.ts +30 -1
- package/dist/types/runtime/host-watch.d.ts +20 -0
- package/dist/types/runtime/icons.d.ts +16 -2
- package/dist/types/runtime/index.d.ts +3 -1
- package/dist/types/runtime/ink.d.ts +8 -0
- package/dist/types/runtime/kind-model.d.ts +14 -0
- package/dist/types/runtime/live-model.d.ts +25 -0
- package/dist/types/runtime/loader.d.ts +5 -11
- package/dist/types/runtime/markup-editor.d.ts +54 -0
- package/dist/types/runtime/markup-model.d.ts +126 -0
- package/dist/types/runtime/markup.d.ts +69 -0
- package/dist/types/runtime/mention-parse.d.ts +18 -0
- package/dist/types/runtime/mentions.d.ts +4 -4
- package/dist/types/runtime/page-background.d.ts +9 -0
- package/dist/types/runtime/page-image.d.ts +123 -0
- package/dist/types/runtime/placement.d.ts +6 -3
- package/dist/types/runtime/record.d.ts +18 -2
- package/dist/types/runtime/related.d.ts +1 -1
- package/dist/types/runtime/replay-model.d.ts +80 -0
- package/dist/types/runtime/replay.d.ts +5 -38
- package/dist/types/runtime/runtime.d.ts +6 -0
- package/dist/types/runtime/screen.d.ts +47 -0
- package/dist/types/runtime/screenshot.d.ts +60 -6
- package/dist/types/runtime/session-source.d.ts +45 -0
- package/dist/types/runtime/session.d.ts +20 -18
- package/dist/types/runtime/shield.d.ts +9 -0
- package/dist/types/runtime/shot.d.ts +45 -0
- package/dist/types/runtime/sketch.d.ts +26 -0
- package/dist/types/runtime/styles.d.ts +1 -1
- package/dist/types/runtime/sync.d.ts +1 -0
- package/dist/types/runtime/thread-model.d.ts +18 -0
- package/dist/types/runtime/ticket-form-model.d.ts +57 -0
- package/dist/types/runtime/ticket-form.d.ts +9 -0
- package/dist/types/runtime/toolbar.d.ts +37 -1
- package/dist/types/runtime/ui-scale.d.ts +17 -0
- package/dist/types/runtime/update.d.ts +37 -0
- package/dist/types/runtime/use-debug-context.d.ts +27 -0
- package/dist/types/server/index.d.ts +22 -0
- package/dist/types/server/support.d.ts +20 -0
- package/dist/types/server.d.ts +2 -0
- package/dist/types/vite/plugin.d.ts +15 -0
- package/dist/vite.js +121 -41
- package/package.json +26 -13
- package/src/capture/api.ts +128 -0
- package/src/capture/editor.tsx +178 -0
- package/src/capture/ignore.ts +38 -0
- package/src/capture/index.ts +27 -0
- package/src/capture/picker.tsx +171 -0
- package/src/capture/picture.ts +90 -0
- package/src/capture/surface.tsx +185 -0
- package/src/compiler/annotate.ts +202 -0
- package/src/compiler/babel.d.ts +11 -0
- package/src/compiler/enabled.ts +55 -0
- package/src/compiler/index.ts +2 -0
- package/src/core/index.ts +8 -0
- package/src/core/resolve.ts +307 -0
- package/src/core/types.ts +58 -0
- package/src/debug-context.ts +47 -0
- package/src/dictation.ts +17 -0
- package/src/embed/controller.ts +215 -0
- package/src/embed/index.ts +68 -0
- package/src/embed/options.ts +100 -0
- package/src/help/index.ts +38 -0
- package/src/help/panel-index.ts +17 -0
- package/src/help/panel-styles.ts +123 -0
- package/src/help/panel.ts +840 -0
- package/src/help/pill-styles.ts +75 -0
- package/src/help/pill.ts +338 -0
- package/src/help/react.tsx +143 -0
- package/src/help/requests.ts +111 -0
- package/src/help/types.ts +124 -0
- package/src/help/wire.ts +55 -0
- package/src/native-wire.ts +124 -0
- package/src/native.ts +282 -0
- package/src/react.ts +27 -0
- package/src/replay.ts +13 -0
- package/src/runtime/api.ts +531 -0
- package/src/runtime/attachment-model.ts +42 -0
- package/src/runtime/attachments.tsx +1260 -0
- package/src/runtime/boot.ts +95 -0
- package/src/runtime/browser-label.ts +31 -0
- package/src/runtime/build-id.ts +10 -0
- package/src/runtime/capture-ui.tsx +306 -0
- package/src/runtime/capture.ts +508 -0
- package/src/runtime/clarity-model.ts +634 -0
- package/src/runtime/clarity-ui.tsx +454 -0
- package/src/runtime/client.ts +151 -0
- package/src/runtime/composer-size.tsx +146 -0
- package/src/runtime/dashboard.tsx +1130 -0
- package/src/runtime/debug-context-core.ts +681 -0
- package/src/runtime/debug-context-host.ts +56 -0
- package/src/runtime/debug-context-model.ts +1319 -0
- package/src/runtime/debug-context-redact.ts +259 -0
- package/src/runtime/debug-context.ts +613 -0
- package/src/runtime/details-model.ts +30 -0
- package/src/runtime/details.tsx +395 -0
- package/src/runtime/dictation-engine.ts +616 -0
- package/src/runtime/dictation-model.ts +79 -0
- package/src/runtime/dictation-ui.tsx +363 -0
- package/src/runtime/dictation.ts +271 -0
- package/src/runtime/dock.ts +108 -0
- package/src/runtime/draft-dock.tsx +124 -0
- package/src/runtime/draft-model.ts +251 -0
- package/src/runtime/draft-store.ts +149 -0
- package/src/runtime/edits.tsx +165 -0
- package/src/runtime/font.ts +42 -0
- package/src/runtime/format.ts +129 -0
- package/src/runtime/freshness.ts +23 -0
- package/src/runtime/handoff.ts +76 -0
- package/src/runtime/host-watch.ts +64 -0
- package/src/runtime/icons.tsx +295 -0
- package/src/runtime/idle.ts +20 -0
- package/src/runtime/index.ts +11 -0
- package/src/runtime/ink.ts +6 -0
- package/src/runtime/kind-model.ts +20 -0
- package/src/runtime/live-model.ts +32 -0
- package/src/runtime/loader.tsx +87 -0
- package/src/runtime/markup-editor.tsx +1333 -0
- package/src/runtime/markup-model.ts +485 -0
- package/src/runtime/markup.ts +382 -0
- package/src/runtime/members.ts +58 -0
- package/src/runtime/mention-parse.ts +56 -0
- package/src/runtime/mentions.tsx +205 -0
- package/src/runtime/page-background.ts +51 -0
- package/src/runtime/page-image.ts +1592 -0
- package/src/runtime/placement.ts +336 -0
- package/src/runtime/record.ts +402 -0
- package/src/runtime/redact.ts +30 -0
- package/src/runtime/related.ts +34 -0
- package/src/runtime/render-page.ts +2 -0
- package/src/runtime/replay-model.ts +232 -0
- package/src/runtime/replay.ts +173 -0
- package/src/runtime/runtime-version.ts +7 -0
- package/src/runtime/runtime.tsx +51 -0
- package/src/runtime/screen.ts +182 -0
- package/src/runtime/screenshot.ts +260 -0
- package/src/runtime/session-source.ts +112 -0
- package/src/runtime/session.ts +108 -0
- package/src/runtime/shield.ts +34 -0
- package/src/runtime/shot.tsx +422 -0
- package/src/runtime/sketch.ts +139 -0
- package/src/runtime/styles.ts +1541 -0
- package/src/runtime/sync.ts +300 -0
- package/src/runtime/thread-model.ts +71 -0
- package/src/runtime/ticket-form-model.ts +128 -0
- package/src/runtime/ticket-form.tsx +342 -0
- package/src/runtime/toolbar.tsx +4427 -0
- package/src/runtime/ui-scale.ts +75 -0
- package/src/runtime/update.ts +98 -0
- package/src/runtime/use-debug-context.ts +81 -0
- package/src/server/index.ts +133 -0
- package/src/server/support.ts +499 -0
- package/src/server.ts +4 -0
- package/src/vite/plugin.ts +102 -0
- package/src/vite.ts +1 -0
- package/dist/chunks/chunk-CSKXLF33.js +0 -153
- package/dist/chunks/chunk-HUM5FQII.js +0 -160
- package/dist/chunks/render-page-76MLIT6M.js +0 -1421
- package/dist/chunks/runtime-JX75TKER.js +0 -9267
- package/dist/chunks/screenshot-66ARTBGR.js +0 -12
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Between checks of the served build while the page stays open. The worker also announces its build over the
|
|
3
|
+
* live socket when its project object starts, and reconnects and tab switches check too; this is the backstop
|
|
4
|
+
* for a toolbar whose project object stayed asleep through a deploy.
|
|
5
|
+
*/
|
|
6
|
+
export declare const CHECK_EVERY_MS = 60000;
|
|
7
|
+
type BuildListener = (build: string) => void;
|
|
8
|
+
/** Called with the build the worker says it serves (a live-socket message). */
|
|
9
|
+
export declare function announceBuild(build: string): void;
|
|
10
|
+
/** Hears every announced build until the returned function is called. */
|
|
11
|
+
export declare function onBuildAnnounced(listener: BuildListener): () => void;
|
|
12
|
+
/**
|
|
13
|
+
* Loads the runtime at `url` and remounts the toolbar from it when it is `build`; resolves false (nothing remounted)
|
|
14
|
+
* when the bundle that came back is some other build. `runtime.tsx#mount` registers it.
|
|
15
|
+
*/
|
|
16
|
+
export type Swapper = (url: string, build: string) => Promise<boolean>;
|
|
17
|
+
export declare function registerSwapper(next: Swapper | null): void;
|
|
18
|
+
/**
|
|
19
|
+
* The bundle URL for `build`. The query defeats the module cache; the chunks it imports carry their own hashes.
|
|
20
|
+
* A page imports a module URL once and keeps it, so each retry (`attempt` > 0) gets a URL of its own.
|
|
21
|
+
*/
|
|
22
|
+
export declare function runtimeUrl(apiUrl: string, build: string, attempt?: number): string;
|
|
23
|
+
/** The build the worker serves now, or null when it can't be read. Never throws. */
|
|
24
|
+
export declare function servedBuild(apiUrl: string, fetchFn?: typeof fetch): Promise<string | null>;
|
|
25
|
+
/** True when the worker serves a different build than the one running here. A "dev" build never swaps. */
|
|
26
|
+
export declare function needsSwap(served: string | null, current?: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Swaps to `build`. Resolves false when no runtime registered, a swap is under way, the bundle that loaded is not
|
|
29
|
+
* `build`, or this page already tried `build` too often.
|
|
30
|
+
*
|
|
31
|
+
* Right after a deploy an edge can still answer `1.js?b=<new>` with the previous bundle. Mounting it would loop:
|
|
32
|
+
* it sees the newer build served, imports the same URL, gets itself back from the module cache and swaps again,
|
|
33
|
+
* remounting the toolbar (and dropping its socket) as fast as it can. The swapper refuses such a bundle instead,
|
|
34
|
+
* and the next try uses a URL this page hasn't imported.
|
|
35
|
+
*/
|
|
36
|
+
export declare function swapRuntime(apiUrl: string, build: string): Promise<boolean>;
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The toolbar's debug context: running while someone is signed in to the toolbar and the host turned it on
|
|
3
|
+
* in code, stopped otherwise (the toolbar's effect calls `start`). The host's functions are read through a ref, so a new options object on every
|
|
4
|
+
* render does not restart the capture and lose what it has seen; only a change of what is captured does.
|
|
5
|
+
*/
|
|
6
|
+
import { type MutableRefObject } from "react";
|
|
7
|
+
import { type DebugCapture, type DebugSnapshotInput } from "./debug-context.js";
|
|
8
|
+
import { type DebugContextConfig } from "./debug-context-model.js";
|
|
9
|
+
export type DebugContextControl = {
|
|
10
|
+
/** The running capture, or null. */
|
|
11
|
+
capture: MutableRefObject<DebugCapture | null>;
|
|
12
|
+
/** Changes when what is captured changes: the effect that calls `start` depends on it. */
|
|
13
|
+
key: string;
|
|
14
|
+
/**
|
|
15
|
+
* Starts the capture and returns its stop. The caller starts it after the activity trail and stops it
|
|
16
|
+
* first, so each puts back exactly the patches it made.
|
|
17
|
+
*/
|
|
18
|
+
start: (apiUrl: string) => () => void;
|
|
19
|
+
};
|
|
20
|
+
export declare function useDebugContext(config: DebugContextConfig | undefined): DebugContextControl;
|
|
21
|
+
/**
|
|
22
|
+
* Builds the context file and uploads it as `context-<stamp>.json`. Resolves with the attachment id, or null
|
|
23
|
+
* when there is none to send or it could not be sent: a report is never held back by its context.
|
|
24
|
+
*/
|
|
25
|
+
export declare function uploadDebugContext(capture: DebugCapture | null, input: DebugSnapshotInput, upload: (blob: Blob, name: string) => Promise<{
|
|
26
|
+
id: string;
|
|
27
|
+
}>): Promise<string | null>;
|
|
@@ -11,4 +11,26 @@ export type CommentServiceConfig = {
|
|
|
11
11
|
linked?: string[];
|
|
12
12
|
};
|
|
13
13
|
export declare function readCommentServiceConfig(env?: Record<string, string | undefined>): CommentServiceConfig | null;
|
|
14
|
+
/**
|
|
15
|
+
* Same-origin session route used by Next.js and TanStack Start.
|
|
16
|
+
* The integration secret stays on the server. The browser only receives a short-lived host token,
|
|
17
|
+
* which still cannot read or write comments until someone signs in to the toolbar.
|
|
18
|
+
*
|
|
19
|
+
* ## The origin is derived, never taken from a caller-chosen header
|
|
20
|
+
*
|
|
21
|
+
* The worker binds a host token to an origin, and it allow-lists every origin registered for the project —
|
|
22
|
+
* which includes linked environments. When this function forwarded the caller's header, any caller could
|
|
23
|
+
* name a *different* registered origin and receive a token bound to it. The header is a request field, so it
|
|
24
|
+
* was never a statement about which site made the request.
|
|
25
|
+
*
|
|
26
|
+
* Two clients legitimately need different answers, and they are distinguishable here:
|
|
27
|
+
*
|
|
28
|
+
* - **A native app** has no browser origin. It declares `<scheme>://app` in `x-comment-service-origin`, and
|
|
29
|
+
* only that *shape* is accepted — never a web origin, which the worker would otherwise honour. Its host
|
|
30
|
+
* proves it is the app by requiring a bearer session before it calls this at all.
|
|
31
|
+
* - **A browser** is bound to the origin the deployment is served from. A page cannot change that by sending a
|
|
32
|
+
* header, so the caller-chosen `origin` is not consulted for a web request at all.
|
|
33
|
+
*/
|
|
34
|
+
/** The session this deployment may bind a token to: the origin it is served from, or one native app scheme. */
|
|
35
|
+
export declare function requestedOrigin(request: Request): string;
|
|
14
36
|
export declare function handleSessionRequest(request: Request, config?: CommentServiceConfig | null): Promise<Response>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type SupportRequestConfig = {
|
|
2
|
+
/** Comment Tool: `https://comment-tool.example.com`. */
|
|
3
|
+
serviceUrl: string;
|
|
4
|
+
/** The host's connection token. Server only: it never reaches the browser. */
|
|
5
|
+
connectionToken: string;
|
|
6
|
+
/** The connection's own id for the project its support requests are filed in. */
|
|
7
|
+
project: string;
|
|
8
|
+
/** Who is asking, from the host's own sign-in. `userId` is the host's id for them. */
|
|
9
|
+
reporter: {
|
|
10
|
+
userId: string;
|
|
11
|
+
name: string;
|
|
12
|
+
};
|
|
13
|
+
/** The reporter's account in the host (a customer, a workspace), shown beside them in the console. */
|
|
14
|
+
account?: {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
};
|
|
18
|
+
fetch?: typeof fetch;
|
|
19
|
+
};
|
|
20
|
+
export declare function handleSupportRequest(request: Request, config: SupportRequestConfig): Promise<Response>;
|
package/dist/types/server.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { handleSessionRequest, readCommentServiceConfig } from "./server/index.js";
|
|
2
2
|
export type { CommentServiceConfig } from "./server/index.js";
|
|
3
|
+
export { handleSupportRequest } from "./server/support.js";
|
|
4
|
+
export type { SupportRequestConfig } from "./server/support.js";
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import type { HostBuild } from "../runtime/debug-context-model.js";
|
|
2
|
+
/** The constant `<CommentToolbar>` reads for the debug context's `build`, when nothing else gave one. */
|
|
3
|
+
export declare const HOST_BUILD_CONSTANT = "__COMMENT_TOOL_HOST_BUILD__";
|
|
4
|
+
/**
|
|
5
|
+
* The build this bundle is: the commit from the CI's variable or from `git`, the version from
|
|
6
|
+
* `COMMENT_SERVICE_BUILD_VERSION` or the package being built, and the mode. Only short, public facts.
|
|
7
|
+
*/
|
|
8
|
+
export declare function hostBuildOf(mode: string, root: string, env?: Record<string, string | undefined>, git?: () => string): HostBuild;
|
|
1
9
|
type ResolvedConfig = {
|
|
2
10
|
command: "build" | "serve";
|
|
3
11
|
mode: string;
|
|
@@ -11,6 +19,13 @@ export declare function commentService(options?: {
|
|
|
11
19
|
}): {
|
|
12
20
|
name: string;
|
|
13
21
|
enforce: "pre";
|
|
22
|
+
config(_config: unknown, environment: {
|
|
23
|
+
mode: string;
|
|
24
|
+
}): {
|
|
25
|
+
define: {
|
|
26
|
+
__COMMENT_TOOL_HOST_BUILD__: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
14
29
|
configResolved(config: ResolvedConfig): void;
|
|
15
30
|
transform(code: string, id: string): {
|
|
16
31
|
code: string;
|
package/dist/vite.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
// src/vite/plugin.ts
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
|
|
1
4
|
// src/compiler/annotate.ts
|
|
2
5
|
import generateModule from "@babel/generator";
|
|
3
6
|
import { parse } from "@babel/parser";
|
|
4
|
-
import traverseModule from "@babel/traverse";
|
|
5
7
|
import * as t from "@babel/types";
|
|
6
8
|
function generate() {
|
|
7
9
|
const mod = generateModule;
|
|
8
10
|
return mod.default ?? mod;
|
|
9
11
|
}
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
return mod.default ?? mod;
|
|
12
|
+
function isWalkNode(value) {
|
|
13
|
+
return value !== null && typeof value === "object" && typeof value.type === "string";
|
|
13
14
|
}
|
|
14
15
|
function toProjectPath(filename, root) {
|
|
15
16
|
const clean = filename.split("?")[0].replaceAll("\\", "/");
|
|
@@ -18,45 +19,73 @@ function toProjectPath(filename, root) {
|
|
|
18
19
|
if (relative.startsWith("/")) relative = relative.slice(1);
|
|
19
20
|
return relative;
|
|
20
21
|
}
|
|
21
|
-
function componentName(
|
|
22
|
-
let
|
|
23
|
-
|
|
24
|
-
if (current.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
function componentName(ancestors, fallback) {
|
|
23
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
24
|
+
const current = ancestors[i];
|
|
25
|
+
if ((current.type === "FunctionDeclaration" || current.type === "ClassDeclaration") && current.id) {
|
|
26
|
+
return current.id.name;
|
|
27
|
+
}
|
|
28
|
+
if (current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") {
|
|
29
|
+
const parent = i > 0 ? ancestors[i - 1] : null;
|
|
30
|
+
if (parent?.type === "VariableDeclarator" && parent.id.type === "Identifier")
|
|
31
|
+
return parent.id.name;
|
|
32
|
+
if ((parent?.type === "ObjectProperty" || parent?.type === "ClassProperty") && parent.key.type === "Identifier") {
|
|
33
|
+
return parent.key.name;
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
|
-
current = current.parentPath;
|
|
35
36
|
}
|
|
36
37
|
return fallback;
|
|
37
38
|
}
|
|
38
|
-
function isRootJsx(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (current.isJSXElement()) return false;
|
|
44
|
-
current = current.parentPath;
|
|
39
|
+
function isRootJsx(ancestors) {
|
|
40
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
41
|
+
const current = ancestors[i];
|
|
42
|
+
if (t.isFunction(current)) return true;
|
|
43
|
+
if (current.type === "JSXElement") return false;
|
|
45
44
|
}
|
|
46
|
-
return
|
|
45
|
+
return false;
|
|
47
46
|
}
|
|
48
|
-
function hasAttr(
|
|
49
|
-
return
|
|
47
|
+
function hasAttr(node, name) {
|
|
48
|
+
return node.openingElement.attributes.some(
|
|
50
49
|
(attr) => attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier" && attr.name.name === name
|
|
51
50
|
);
|
|
52
51
|
}
|
|
53
|
-
function setAttr(
|
|
54
|
-
if (hasAttr(
|
|
55
|
-
|
|
52
|
+
function setAttr(node, name, value) {
|
|
53
|
+
if (hasAttr(node, name)) return;
|
|
54
|
+
node.openingElement.attributes.push(
|
|
56
55
|
t.jsxAttribute(t.jsxIdentifier(name), t.stringLiteral(value))
|
|
57
56
|
);
|
|
58
57
|
}
|
|
59
|
-
var
|
|
58
|
+
var REACT_BUILTINS = /* @__PURE__ */ new Set([
|
|
59
|
+
"Fragment",
|
|
60
|
+
"Suspense",
|
|
61
|
+
"StrictMode",
|
|
62
|
+
"Profiler",
|
|
63
|
+
"Activity",
|
|
64
|
+
"ViewTransition"
|
|
65
|
+
]);
|
|
66
|
+
var contextComponent = /\.(?:Provider|Consumer)$/;
|
|
67
|
+
function reactBuiltinTags(program) {
|
|
68
|
+
const tags = /* @__PURE__ */ new Set();
|
|
69
|
+
const namespaces = /* @__PURE__ */ new Set(["React"]);
|
|
70
|
+
for (const statement of program.body ?? []) {
|
|
71
|
+
if (statement.type !== "ImportDeclaration" || statement.source?.value !== "react") continue;
|
|
72
|
+
for (const specifier of statement.specifiers ?? []) {
|
|
73
|
+
const local = specifier.local?.name;
|
|
74
|
+
if (typeof local !== "string") continue;
|
|
75
|
+
if (specifier.type === "ImportNamespaceSpecifier" || specifier.type === "ImportDefaultSpecifier") {
|
|
76
|
+
namespaces.add(local);
|
|
77
|
+
} else if (specifier.type === "ImportSpecifier") {
|
|
78
|
+
const imported = specifier.imported?.name ?? specifier.imported?.value;
|
|
79
|
+
if (REACT_BUILTINS.has(imported)) tags.add(local);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const builtin of REACT_BUILTINS) {
|
|
84
|
+
tags.add(builtin);
|
|
85
|
+
for (const namespace of namespaces) tags.add(`${namespace}.${builtin}`);
|
|
86
|
+
}
|
|
87
|
+
return tags;
|
|
88
|
+
}
|
|
60
89
|
function jsxName(name) {
|
|
61
90
|
if (t.isJSXIdentifier(name)) return name.name;
|
|
62
91
|
if (t.isJSXMemberExpression(name)) {
|
|
@@ -80,18 +109,32 @@ function annotateSource(code, filename, root) {
|
|
|
80
109
|
const file = toProjectPath(filename, root);
|
|
81
110
|
const fallback = file.split("/").pop()?.replace(/\.[jt]sx$/, "") || "component";
|
|
82
111
|
let changed = false;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
112
|
+
const ancestors = [];
|
|
113
|
+
const builtins = reactBuiltinTags(ast.program);
|
|
114
|
+
const stamp = (node) => {
|
|
115
|
+
const tag = jsxName(node.openingElement.name);
|
|
116
|
+
if (!tag || builtins.has(tag) || contextComponent.test(tag)) return;
|
|
117
|
+
if (!isRootJsx(ancestors)) return;
|
|
118
|
+
const name = componentName(ancestors, fallback);
|
|
119
|
+
const before = node.openingElement.attributes.length;
|
|
120
|
+
setAttr(node, "data-cs-cmp", name);
|
|
121
|
+
setAttr(node, "data-cs-file", file);
|
|
122
|
+
if (node.openingElement.attributes.length !== before) changed = true;
|
|
123
|
+
};
|
|
124
|
+
const visit = (node) => {
|
|
125
|
+
if (node.type === "JSXElement") stamp(node);
|
|
126
|
+
ancestors.push(node);
|
|
127
|
+
for (const key of t.VISITOR_KEYS[node.type] ?? []) {
|
|
128
|
+
const child = node[key];
|
|
129
|
+
if (Array.isArray(child)) {
|
|
130
|
+
for (const item of child) if (isWalkNode(item)) visit(item);
|
|
131
|
+
} else if (isWalkNode(child)) {
|
|
132
|
+
visit(child);
|
|
133
|
+
}
|
|
93
134
|
}
|
|
94
|
-
|
|
135
|
+
ancestors.pop();
|
|
136
|
+
};
|
|
137
|
+
visit(ast);
|
|
95
138
|
if (!changed) return code;
|
|
96
139
|
const printed = generate()(ast, { retainLines: true, comments: true }, code);
|
|
97
140
|
return printed.code;
|
|
@@ -132,6 +175,37 @@ function buildConfigured(root, mode, env = process.env) {
|
|
|
132
175
|
}
|
|
133
176
|
|
|
134
177
|
// src/vite/plugin.ts
|
|
178
|
+
var HOST_BUILD_CONSTANT = "__COMMENT_TOOL_HOST_BUILD__";
|
|
179
|
+
var COMMIT_VARIABLES = [
|
|
180
|
+
"COMMENT_SERVICE_BUILD_COMMIT",
|
|
181
|
+
"GITHUB_SHA",
|
|
182
|
+
"CF_PAGES_COMMIT_SHA",
|
|
183
|
+
"WORKERS_CI_COMMIT_SHA",
|
|
184
|
+
"VERCEL_GIT_COMMIT_SHA",
|
|
185
|
+
"CI_COMMIT_SHA"
|
|
186
|
+
];
|
|
187
|
+
function hostBuildOf(mode, root, env = process.env, git = () => execFileSync("git", ["rev-parse", "--short=12", "HEAD"], {
|
|
188
|
+
cwd: root,
|
|
189
|
+
encoding: "utf8",
|
|
190
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
191
|
+
})) {
|
|
192
|
+
let commit = COMMIT_VARIABLES.map((name) => env[name]?.trim()).find(Boolean);
|
|
193
|
+
if (!commit) {
|
|
194
|
+
try {
|
|
195
|
+
commit = git().trim();
|
|
196
|
+
} catch {
|
|
197
|
+
commit = void 0;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const short = commit && /^[0-9a-f]{7,40}$/i.test(commit) ? commit.slice(0, 12) : void 0;
|
|
201
|
+
const version = (env.COMMENT_SERVICE_BUILD_VERSION ?? env.npm_package_version)?.trim().slice(0, 64);
|
|
202
|
+
return {
|
|
203
|
+
...short ? { commit: short } : {},
|
|
204
|
+
...version ? { version } : {},
|
|
205
|
+
env: mode.slice(0, 40),
|
|
206
|
+
builtAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
207
|
+
};
|
|
208
|
+
}
|
|
135
209
|
function commentService(options) {
|
|
136
210
|
const root = (options?.root ?? process.cwd()).replace(/\/$/, "");
|
|
137
211
|
let enabled = options?.enabled ?? process.env.COMMENT_SERVICE_ENABLED !== "0";
|
|
@@ -139,6 +213,12 @@ function commentService(options) {
|
|
|
139
213
|
return {
|
|
140
214
|
name: "devaccel-comment-service",
|
|
141
215
|
enforce: "pre",
|
|
216
|
+
// The build's commit and version, for the debug context of each comment (`<CommentToolbar debugContext>`).
|
|
217
|
+
config(_config, environment) {
|
|
218
|
+
return {
|
|
219
|
+
define: { [HOST_BUILD_CONSTANT]: JSON.stringify(hostBuildOf(environment.mode, root)) }
|
|
220
|
+
};
|
|
221
|
+
},
|
|
142
222
|
configResolved(config) {
|
|
143
223
|
enabled = stampingEnabled({
|
|
144
224
|
option: options?.enabled,
|
package/package.json
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devaccel-labs/app-comments-toolbar",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "In-app comment toolbar for host apps
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "In-app comment toolbar for host apps: Vite plugin, <CommentToolbar>, and the same-origin session route.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/DevAccel-Labs/comment_tool.git",
|
|
9
|
+
"directory": "packages/toolbar"
|
|
10
|
+
},
|
|
6
11
|
"files": [
|
|
7
12
|
"dist",
|
|
13
|
+
"src",
|
|
8
14
|
"README.md"
|
|
9
15
|
],
|
|
10
16
|
"type": "module",
|
|
@@ -21,6 +27,12 @@
|
|
|
21
27
|
"types": "./dist/server.d.ts",
|
|
22
28
|
"default": "./dist/server.js"
|
|
23
29
|
},
|
|
30
|
+
"./dictation": "./src/dictation.ts",
|
|
31
|
+
"./replay": "./src/replay.ts",
|
|
32
|
+
"./debug-context": "./src/debug-context.ts",
|
|
33
|
+
"./debug-context-host": "./src/runtime/debug-context-host.ts",
|
|
34
|
+
"./native-wire": "./src/native-wire.ts",
|
|
35
|
+
"./native": "./src/native.ts",
|
|
24
36
|
"./package.json": "./package.json"
|
|
25
37
|
},
|
|
26
38
|
"publishConfig": {
|
|
@@ -29,26 +41,27 @@
|
|
|
29
41
|
},
|
|
30
42
|
"scripts": {
|
|
31
43
|
"build": "bun build.mjs",
|
|
32
|
-
"build:runtime": "bun ../../scripts/
|
|
33
|
-
"typecheck": "tsc --noEmit --incremental --tsBuildInfoFile tsconfig.tsbuildinfo"
|
|
34
|
-
"lint": "bun ../../scripts/lint-package.mjs",
|
|
35
|
-
"lint:type-aware": "bun ../../scripts/lint-package.mjs --type-aware",
|
|
36
|
-
"format:check": "oxfmt --ignore-path=../../.oxfmtignore --check package.json tsconfig.json src",
|
|
37
|
-
"lint:fix": "bun ../../scripts/lint-package.mjs --fix",
|
|
38
|
-
"format": "oxfmt --ignore-path=../../.oxfmtignore package.json tsconfig.json src"
|
|
44
|
+
"build:runtime": "bun ../../scripts/build-toolbar.mjs",
|
|
45
|
+
"typecheck": "tsc --noEmit --incremental --tsBuildInfoFile tsconfig.tsbuildinfo && tsc --noEmit -p tests"
|
|
39
46
|
},
|
|
40
47
|
"dependencies": {
|
|
41
48
|
"@babel/generator": "^7.28.5",
|
|
42
49
|
"@babel/parser": "^7.28.5",
|
|
43
|
-
"@babel/
|
|
44
|
-
"@babel/types": "^7.28.5",
|
|
45
|
-
"modern-screenshot": "4.7.0"
|
|
50
|
+
"@babel/types": "^7.28.5"
|
|
46
51
|
},
|
|
47
52
|
"devDependencies": {
|
|
48
|
-
"
|
|
53
|
+
"@fontsource-variable/inter": "^5.3.0",
|
|
54
|
+
"esbuild": "0.28.1",
|
|
55
|
+
"modern-screenshot": "4.7.0",
|
|
56
|
+
"radix-ui": "1.4.3"
|
|
49
57
|
},
|
|
50
58
|
"peerDependencies": {
|
|
51
59
|
"react": ">=18",
|
|
52
60
|
"react-dom": ">=18"
|
|
61
|
+
},
|
|
62
|
+
"peerDependenciesMeta": {
|
|
63
|
+
"react-dom": {
|
|
64
|
+
"optional": true
|
|
65
|
+
}
|
|
53
66
|
}
|
|
54
67
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `window.CommentToolCapture`: the toolbar's capture tools for a host page that has its own place to put what
|
|
3
|
+
* they produce (a support form, say). There is no session here and nothing is sent anywhere: this folder
|
|
4
|
+
* imports none of the toolbar's sign-in, storage or `/v1` code (the build checks that), and each call resolves
|
|
5
|
+
* with files for the host to keep or drop.
|
|
6
|
+
*
|
|
7
|
+
* One capture at a time. A call made while another is open closes that one, which resolves null.
|
|
8
|
+
*/
|
|
9
|
+
import { createElement } from "react";
|
|
10
|
+
import { captureAnchor } from "../runtime/capture.js";
|
|
11
|
+
import { targetName } from "../runtime/format.js";
|
|
12
|
+
import { Editor } from "./editor.js";
|
|
13
|
+
import { Capturing, Picker } from "./picker.js";
|
|
14
|
+
import { pictureOf, shootPage, type CapturedImage } from "./picture.js";
|
|
15
|
+
import { openSurface, type CaptureTheme } from "./surface.js";
|
|
16
|
+
|
|
17
|
+
export type { CapturedImage } from "./picture.js";
|
|
18
|
+
export type { CaptureTheme } from "./surface.js";
|
|
19
|
+
|
|
20
|
+
export type CaptureOptions = {
|
|
21
|
+
/** The look of the capture UI. `auto` (the default) follows the system's light or dark setting. */
|
|
22
|
+
theme?: CaptureTheme;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type CapturedTarget = {
|
|
26
|
+
/** What a person would call it: the toolbar's own name for an anchor ("button › Save"). */
|
|
27
|
+
label: string;
|
|
28
|
+
/** The page's path. */
|
|
29
|
+
route: string;
|
|
30
|
+
/** The visible page with the element outlined, or null when the page could not be drawn. */
|
|
31
|
+
image: CapturedImage | null;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type CommentToolCaptureApi = {
|
|
35
|
+
/**
|
|
36
|
+
* Takes a picture of the page as it is now and opens the toolbar's drawing editor over it. Resolves with
|
|
37
|
+
* the picture, drawn on or not, when the person presses Done; null when they close it. Rejects when the
|
|
38
|
+
* page could not be drawn.
|
|
39
|
+
*/
|
|
40
|
+
screenshot(options?: CaptureOptions): Promise<CapturedImage | null>;
|
|
41
|
+
/**
|
|
42
|
+
* Lets the person point at one thing on the page, as the toolbar's "comment on this" does. Resolves with
|
|
43
|
+
* what they pointed at and a picture of the page with it outlined; null on Escape.
|
|
44
|
+
*/
|
|
45
|
+
pick(options?: CaptureOptions): Promise<CapturedTarget | null>;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const THEMES: readonly CaptureTheme[] = ["light", "dark", "auto"];
|
|
49
|
+
|
|
50
|
+
function themeOf(options: CaptureOptions | undefined): CaptureTheme {
|
|
51
|
+
const theme = options?.theme ?? "auto";
|
|
52
|
+
if (!THEMES.includes(theme)) {
|
|
53
|
+
throw new TypeError(
|
|
54
|
+
`CommentToolCapture: theme must be "light", "dark" or "auto", not ${JSON.stringify(theme)}.`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return theme;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function createCapture(): CommentToolCaptureApi {
|
|
61
|
+
/** Closes the capture that is open, resolving its promise with null. */
|
|
62
|
+
let closeOpen: (() => void) | null = null;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Runs one capture: `start` gets the surface to draw in and `settle`, which closes the surface and settles
|
|
66
|
+
* the call. Anything after the first `settle` is ignored.
|
|
67
|
+
*/
|
|
68
|
+
function capture<T>(
|
|
69
|
+
theme: CaptureTheme,
|
|
70
|
+
start: (
|
|
71
|
+
surface: ReturnType<typeof openSurface>,
|
|
72
|
+
settle: (outcome: { value: T | null } | { error: Error }) => void,
|
|
73
|
+
) => void,
|
|
74
|
+
): Promise<T | null> {
|
|
75
|
+
closeOpen?.();
|
|
76
|
+
return new Promise<T | null>((resolve, reject) => {
|
|
77
|
+
let settled = false;
|
|
78
|
+
const settle = (outcome: { value: T | null } | { error: Error }) => {
|
|
79
|
+
if (settled) return;
|
|
80
|
+
settled = true;
|
|
81
|
+
if (closeOpen === cancel) closeOpen = null;
|
|
82
|
+
surface.close();
|
|
83
|
+
if ("error" in outcome) reject(outcome.error);
|
|
84
|
+
else resolve(outcome.value);
|
|
85
|
+
};
|
|
86
|
+
const cancel = () => settle({ value: null });
|
|
87
|
+
const surface = openSurface(theme, cancel);
|
|
88
|
+
closeOpen = cancel;
|
|
89
|
+
start(surface, settle);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
async screenshot(options) {
|
|
95
|
+
const theme = themeOf(options);
|
|
96
|
+
// Copied before anything of this script is on the page, in the same task as the call.
|
|
97
|
+
const shot = shootPage(null);
|
|
98
|
+
return capture<CapturedImage>(theme, (surface, settle) => {
|
|
99
|
+
const cancel = () => settle({ value: null });
|
|
100
|
+
const done = (image: CapturedImage) => settle({ value: image });
|
|
101
|
+
surface.show(createElement(Editor, { picture: null, onDone: done, onCancel: cancel }));
|
|
102
|
+
void pictureOf(shot).then((picture) => {
|
|
103
|
+
if (!picture) {
|
|
104
|
+
settle({ error: new Error("CommentToolCapture: the page could not be captured.") });
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
surface.show(createElement(Editor, { picture, onDone: done, onCancel: cancel }));
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
async pick(options) {
|
|
113
|
+
const theme = themeOf(options);
|
|
114
|
+
return capture<CapturedTarget>(theme, (surface, settle) => {
|
|
115
|
+
const onPick = (element: Element) => {
|
|
116
|
+
const anchor = captureAnchor(element, window.location.pathname);
|
|
117
|
+
// Copied in the click itself, so the picture is the page the person was looking at.
|
|
118
|
+
const shot = shootPage(element);
|
|
119
|
+
surface.show(createElement(Capturing));
|
|
120
|
+
void pictureOf(shot).then((image) =>
|
|
121
|
+
settle({ value: { label: targetName(anchor), route: anchor.route, image } }),
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
surface.show(createElement(Picker, { onPick, onCancel: () => settle({ value: null }) }));
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|