reflet-sdk 0.7.6 → 0.7.7

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 (33) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +13 -5
  3. package/dist/desktop.d.ts.map +1 -1
  4. package/dist/desktop.js +3 -2
  5. package/dist/desktop.js.map +1 -1
  6. package/dist/devtools-layer-CYIoe3Vo.js +372 -0
  7. package/dist/devtools-layer-CYIoe3Vo.js.map +1 -0
  8. package/dist/devtools-next.js +1 -1
  9. package/dist/devtools-vite.js +3 -3
  10. package/dist/feedback.js +4 -5
  11. package/dist/feedback.js.map +1 -1
  12. package/dist/{handler-ndF3gZBx.js → handler-1XhfZvZ0.js} +3 -3
  13. package/dist/{handler-ndF3gZBx.js.map → handler-1XhfZvZ0.js.map} +1 -1
  14. package/dist/{protocol-cJVVwnPe.js → protocol-BnurX0CF.js} +2 -1
  15. package/dist/protocol-BnurX0CF.js.map +1 -0
  16. package/dist/{reflet-proxy-BW6q9xds.js → reflet-proxy-DVE6ov2f.js} +14 -7
  17. package/dist/reflet-proxy-DVE6ov2f.js.map +1 -0
  18. package/dist/{submit-DsJwTy3X.js → submit-r3_M1ITo.js} +49 -3
  19. package/dist/submit-r3_M1ITo.js.map +1 -0
  20. package/dist/{team-layer-VlQtvR5O.js → team-layer-5-uPYTjV.js} +162 -33
  21. package/dist/team-layer-5-uPYTjV.js.map +1 -0
  22. package/dist/{devtools-styles-DQpiubGB.js → use-sheet-tab-CacYFVjI.js} +4232 -673
  23. package/dist/use-sheet-tab-CacYFVjI.js.map +1 -0
  24. package/package.json +1 -1
  25. package/dist/devtools-layer-xN47FKpW.js +0 -3818
  26. package/dist/devtools-layer-xN47FKpW.js.map +0 -1
  27. package/dist/devtools-styles-DQpiubGB.js.map +0 -1
  28. package/dist/protocol-cJVVwnPe.js.map +0 -1
  29. package/dist/reflet-proxy-BW6q9xds.js.map +0 -1
  30. package/dist/selection-outline-NzAAAZXR.js +0 -51
  31. package/dist/selection-outline-NzAAAZXR.js.map +0 -1
  32. package/dist/submit-DsJwTy3X.js.map +0 -1
  33. package/dist/team-layer-VlQtvR5O.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,19 @@ All notable changes to `reflet-sdk` will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.7.7] - 2026-10-17
9
+
10
+ ### Added
11
+
12
+ - Team tools in desktop apps: the bar now opens the full panel, with Board, Notes and Sessions. Members pick an element, note it and add it to the board, or read the board's feedback, all through the bridge. There is no Code tab and no source lookup, since a production build has no dev server. Team tools in a browser stay Sessions only.
13
+
14
+ ### Changed
15
+
16
+ - Team tools: the bar's grip tooltip names the organization you are signed in to.
17
+ - Desktop sign-in: the reflet.app page says "Sign in to a desktop app" and "Go back to the app" instead of asking to connect a dev server.
18
+ - Sessions: Prepare brief shows the brief right away, with Copy brief and Download .zip. Ticking or unticking an intention or a screenshot updates it in place; Build brief and Edit selection are gone. The brief is only saved under Earlier briefs once you copy or download it. Requires the matching Reflet backend.
19
+ - Sessions: until a finished session is processed, the review lists its steps in order (upload, voice transcription, finding intentions with AI, or the comparison with the revision it continues), each marked done, running, waiting for the one before, or failed with its reason. A session whose analysis is still waiting to upload no longer offers Analyze. The session list says "Finding intentions" instead of "Reading".
20
+
8
21
  ## [0.7.6] - 2026-10-17
9
22
 
10
23
  ### Changed
package/README.md CHANGED
@@ -215,12 +215,14 @@ users. Pass `devtools={false}` to turn it off.
215
215
  source location, and a code search for the element's text.
216
216
  - **Sessions** — talk through the app while you use it: Reflet records your
217
217
  voice, what you point at and mark, and a screenshot at the start, at each
218
- mark and on each page change. Finish, and Reflet transcribes the session and
219
- proposes the intentions behind what you said. Confirm, edit or dismiss each
218
+ mark and on each page change. Finish, and Reflet uploads and transcribes the
219
+ session, then its AI proposes the intentions behind what you said; the review
220
+ shows which of these steps is running. Confirm, edit or dismiss each
220
221
  one. Like notes, confirmed intentions have two ways out: **Prepare brief**
221
- builds a brief you copy into a coding agent or download as a `.zip` with its
222
- screenshots (nothing is shared until you copy or download it), and **Add to
223
- board** turns them into internal feedback, one by one or all at once.
222
+ shows the brief right away, ready to copy into a coding agent or download as
223
+ a `.zip` with its screenshots (nothing is shared until you copy or download
224
+ it), and **Add to board** turns them into internal feedback, one by one or
225
+ all at once.
224
226
  Sessions are recorded under your own name, so they need Connect rather than
225
227
  `REFLET_SECRET_KEY`.
226
228
 
@@ -330,6 +332,12 @@ Expose the four calls from the preload script, then pass them as the bridge:
330
332
  at once, so only pass the bridge in builds meant for your team. `request` only
331
333
  forwards the session and feedback endpoints the devtools use.
332
334
 
335
+ A desktop app gets more than sessions: the bar opens the devtools panel with
336
+ Board, Notes and Sessions, so members can pick an element, note it, add it to
337
+ the board, and read the board's feedback. Everything that needs your source
338
+ code (the Code tab, source locations you can open) stays in development. The
339
+ browser flow above records sessions only.
340
+
333
341
  ### 5. In-App Surveys
334
342
 
335
343
  Create surveys in the Reflet dashboard (NPS, ratings, choices, text, statements,
@@ -1 +1 @@
1
- {"version":3,"file":"desktop.d.ts","names":[],"sources":["../src/devtools/server/desktop.ts"],"mappings":";;iBA4BiB;;EAEf;;EAEA;;EAEA;;EAEA,eAAe,gBAAgB;EAC/B;;;;;;;;;wBAsGc,oBACd,SAAS,uBACR"}
1
+ {"version":3,"file":"desktop.d.ts","names":[],"sources":["../src/devtools/server/desktop.ts"],"mappings":";;iBA4BiB;;EAEf;;EAEA;;EAEA;;EAEA,eAAe,gBAAgB;EAC/B;;;;;;;;;wBAuGc,oBACd,SAAS,uBACR"}
package/dist/desktop.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as connectCallbackUriFor, f as PENDING_CONNECT_TTL_MS, i as connectAuthorizeUrl, l as forgetConnection, m as readConnection, o as connectOutcomePage, p as htmlPage, r as resolveDevtoolsOptions, t as proxyWithBoardCredential, u as redeemConnectCode } from "./reflet-proxy-BW6q9xds.js";
1
+ import { a as connectCallbackUriFor, f as PENDING_CONNECT_TTL_MS, i as connectAuthorizeUrl, l as forgetConnection, m as readConnection, o as connectOutcomePage, p as htmlPage, r as resolveDevtoolsOptions, t as proxyWithBoardCredential, u as redeemConnectCode } from "./reflet-proxy-DVE6ov2f.js";
2
2
  import { createServer } from "node:http";
3
3
  //#region src/devtools/server/desktop.ts
4
4
  const LOOPBACK_HOST = "127.0.0.1";
@@ -28,7 +28,7 @@ function awaitConnectCallback(server, pending) {
28
28
  redirectUri: pending.redirectUri,
29
29
  state: pending.state
30
30
  });
31
- await sendPage(response, connectOutcomePage(outcome));
31
+ await sendPage(response, connectOutcomePage(outcome, "desktopApp"));
32
32
  if (!outcome.ok) throw new Error(outcome.message);
33
33
  return { organizationName: outcome.organizationName };
34
34
  };
@@ -50,6 +50,7 @@ async function startBrowserSignIn(settings, publicKey) {
50
50
  const port = await listenOnLoopback(server);
51
51
  const redirectUri = connectCallbackUriFor(`http://${LOOPBACK_HOST}:${port}`);
52
52
  const { authorizeUrl, state } = connectAuthorizeUrl(settings, {
53
+ client: "desktopApp",
53
54
  publicKey,
54
55
  redirectUri
55
56
  });
@@ -1 +1 @@
1
- {"version":3,"file":"desktop.js","names":[],"sources":["../src/devtools/server/desktop.ts"],"sourcesContent":["import { createServer, type Server, type ServerResponse } from \"node:http\";\nimport type {\n TeamToolsAccount,\n TeamToolsBridge,\n} from \"../../sessions/team/team-tools-bridge\";\nimport {\n connectAuthorizeUrl,\n connectCallbackUriFor,\n connectOutcomePage,\n forgetConnection,\n redeemConnectCode,\n} from \"./connect/connect-routes\";\nimport { readConnection } from \"./connect/connection-store\";\nimport { htmlPage } from \"./connect/html-page\";\nimport { PENDING_CONNECT_TTL_MS } from \"./connect/pending-connects\";\nimport {\n type ResolvedDevtoolsOptions,\n resolveDevtoolsOptions,\n} from \"./options\";\nimport { proxyWithBoardCredential } from \"./reflet-proxy\";\n\nexport type {\n TeamToolsAccount,\n TeamToolsBridge,\n TeamToolsCall,\n TeamToolsReply,\n} from \"../../sessions/team/team-tools-bridge\";\n\nexport interface TeamToolsHostOptions {\n /** Reflet API origin. Defaults to the hosted API. */\n apiUrl?: string;\n /** A directory only this app uses, such as Electron's `app.getPath(\"userData\")`. Keeps its sign-in apart from other apps and dev servers. */\n appDataDirectory: string;\n /** Reflet web app origin sign-in happens on. Defaults to https://www.reflet.app. */\n appUrl?: string;\n /** Opens a URL in the system browser, such as Electron's `shell.openExternal`. */\n openExternal: (url: string) => Promise<void>;\n publicKey: string;\n}\n\ninterface BrowserSignIn {\n account: Promise<TeamToolsAccount>;\n authorizeUrl: string;\n}\n\nconst LOOPBACK_HOST = \"127.0.0.1\";\nconst SIGN_IN_TIMED_OUT = \"Reflet sign-in timed out. Sign in again.\";\n\nasync function sendPage(response: ServerResponse, page: Response) {\n response.writeHead(page.status, Object.fromEntries(page.headers));\n response.end(await page.text());\n}\n\nasync function listenOnLoopback(server: Server): Promise<number> {\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(0, LOOPBACK_HOST, resolve);\n });\n const address = server.address();\n if (address === null || typeof address === \"string\") {\n throw new Error(\"The Reflet sign-in listener has no port.\");\n }\n return address.port;\n}\n\n/** Settles on the first callback carrying this sign-in's state; anything else gets a 404 and changes nothing. */\nfunction awaitConnectCallback(\n server: Server,\n pending: {\n redirectUri: string;\n settings: ResolvedDevtoolsOptions;\n state: string;\n }\n): Promise<TeamToolsAccount> {\n const callbackPath = new URL(pending.redirectUri).pathname;\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(\n () => reject(new Error(SIGN_IN_TIMED_OUT)),\n PENDING_CONNECT_TTL_MS\n );\n let isRedeeming = false;\n const redeem = async (url: URL, response: ServerResponse) => {\n const outcome = await redeemConnectCode(pending.settings, {\n code: url.searchParams.get(\"code\"),\n redirectUri: pending.redirectUri,\n state: pending.state,\n });\n await sendPage(response, connectOutcomePage(outcome));\n if (!outcome.ok) {\n throw new Error(outcome.message);\n }\n return { organizationName: outcome.organizationName };\n };\n server.on(\"request\", (request, response) => {\n const url = new URL(request.url ?? \"/\", pending.redirectUri);\n const isThisSignIn =\n request.method === \"GET\" &&\n url.pathname === callbackPath &&\n url.searchParams.get(\"state\") === pending.state;\n if (isRedeeming || !isThisSignIn) {\n sendPage(\n response,\n htmlPage(\"Not found\", \"Nothing here answers this link.\", 404)\n ).catch(() => undefined);\n return;\n }\n isRedeeming = true;\n clearTimeout(timeout);\n redeem(url, response).then(resolve, reject);\n });\n });\n}\n\nasync function startBrowserSignIn(\n settings: ResolvedDevtoolsOptions,\n publicKey: string\n): Promise<BrowserSignIn> {\n const server = createServer();\n const port = await listenOnLoopback(server);\n const redirectUri = connectCallbackUriFor(`http://${LOOPBACK_HOST}:${port}`);\n const { authorizeUrl, state } = connectAuthorizeUrl(settings, {\n publicKey,\n redirectUri,\n });\n const account = awaitConnectCallback(server, {\n redirectUri,\n settings,\n state,\n }).finally(() => server.close());\n return { account, authorizeUrl };\n}\n\n/**\n * Runs team tools for a desktop app in its privileged process (Electron's\n * main): sign-in goes through the system browser back to a one-shot loopback\n * listener, and the token stays on disk outside the page, like the dev\n * server's Connect. Expose the returned bridge to the page over IPC and pass it\n * to `<RefletFeedback teamTools={bridge} />`.\n */\nexport function createTeamToolsHost(\n options: TeamToolsHostOptions\n): TeamToolsBridge {\n const settings = resolveDevtoolsOptions(\n {\n apiUrl: options.apiUrl,\n appUrl: options.appUrl,\n root: options.appDataDirectory,\n },\n {}\n );\n let pendingSignIn: Promise<BrowserSignIn> | null = null;\n\n const currentSignIn = (): Promise<BrowserSignIn> => {\n if (pendingSignIn) {\n return pendingSignIn;\n }\n const started = startBrowserSignIn(settings, options.publicKey);\n pendingSignIn = started;\n const release = () => {\n if (pendingSignIn === started) {\n pendingSignIn = null;\n }\n };\n started.then((signIn) => signIn.account).then(release, release);\n return started;\n };\n\n return {\n account: async () => {\n const connection = await readConnection(settings.root, settings.apiUrl);\n return connection && { organizationName: connection.organizationName };\n },\n request: async (call) => {\n const search = call.query ? `?${new URLSearchParams(call.query)}` : \"\";\n const response = await proxyWithBoardCredential(\n new Request(`${settings.apiUrl}/${search}`, {\n body: call.method === \"POST\" ? call.body : undefined,\n method: call.method,\n }),\n call.path,\n settings\n );\n return { body: await response.text(), status: response.status };\n },\n signIn: async () => {\n const signIn = await currentSignIn();\n await options.openExternal(signIn.authorizeUrl);\n return await signIn.account;\n },\n signOut: async () => {\n await forgetConnection(settings);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"desktop.js","names":[],"sources":["../src/devtools/server/desktop.ts"],"sourcesContent":["import { createServer, type Server, type ServerResponse } from \"node:http\";\nimport type {\n TeamToolsAccount,\n TeamToolsBridge,\n} from \"../../sessions/team/team-tools-bridge\";\nimport {\n connectAuthorizeUrl,\n connectCallbackUriFor,\n connectOutcomePage,\n forgetConnection,\n redeemConnectCode,\n} from \"./connect/connect-routes\";\nimport { readConnection } from \"./connect/connection-store\";\nimport { htmlPage } from \"./connect/html-page\";\nimport { PENDING_CONNECT_TTL_MS } from \"./connect/pending-connects\";\nimport {\n type ResolvedDevtoolsOptions,\n resolveDevtoolsOptions,\n} from \"./options\";\nimport { proxyWithBoardCredential } from \"./reflet-proxy\";\n\nexport type {\n TeamToolsAccount,\n TeamToolsBridge,\n TeamToolsCall,\n TeamToolsReply,\n} from \"../../sessions/team/team-tools-bridge\";\n\nexport interface TeamToolsHostOptions {\n /** Reflet API origin. Defaults to the hosted API. */\n apiUrl?: string;\n /** A directory only this app uses, such as Electron's `app.getPath(\"userData\")`. Keeps its sign-in apart from other apps and dev servers. */\n appDataDirectory: string;\n /** Reflet web app origin sign-in happens on. Defaults to https://www.reflet.app. */\n appUrl?: string;\n /** Opens a URL in the system browser, such as Electron's `shell.openExternal`. */\n openExternal: (url: string) => Promise<void>;\n publicKey: string;\n}\n\ninterface BrowserSignIn {\n account: Promise<TeamToolsAccount>;\n authorizeUrl: string;\n}\n\nconst LOOPBACK_HOST = \"127.0.0.1\";\nconst SIGN_IN_TIMED_OUT = \"Reflet sign-in timed out. Sign in again.\";\n\nasync function sendPage(response: ServerResponse, page: Response) {\n response.writeHead(page.status, Object.fromEntries(page.headers));\n response.end(await page.text());\n}\n\nasync function listenOnLoopback(server: Server): Promise<number> {\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(0, LOOPBACK_HOST, resolve);\n });\n const address = server.address();\n if (address === null || typeof address === \"string\") {\n throw new Error(\"The Reflet sign-in listener has no port.\");\n }\n return address.port;\n}\n\n/** Settles on the first callback carrying this sign-in's state; anything else gets a 404 and changes nothing. */\nfunction awaitConnectCallback(\n server: Server,\n pending: {\n redirectUri: string;\n settings: ResolvedDevtoolsOptions;\n state: string;\n }\n): Promise<TeamToolsAccount> {\n const callbackPath = new URL(pending.redirectUri).pathname;\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(\n () => reject(new Error(SIGN_IN_TIMED_OUT)),\n PENDING_CONNECT_TTL_MS\n );\n let isRedeeming = false;\n const redeem = async (url: URL, response: ServerResponse) => {\n const outcome = await redeemConnectCode(pending.settings, {\n code: url.searchParams.get(\"code\"),\n redirectUri: pending.redirectUri,\n state: pending.state,\n });\n await sendPage(response, connectOutcomePage(outcome, \"desktopApp\"));\n if (!outcome.ok) {\n throw new Error(outcome.message);\n }\n return { organizationName: outcome.organizationName };\n };\n server.on(\"request\", (request, response) => {\n const url = new URL(request.url ?? \"/\", pending.redirectUri);\n const isThisSignIn =\n request.method === \"GET\" &&\n url.pathname === callbackPath &&\n url.searchParams.get(\"state\") === pending.state;\n if (isRedeeming || !isThisSignIn) {\n sendPage(\n response,\n htmlPage(\"Not found\", \"Nothing here answers this link.\", 404)\n ).catch(() => undefined);\n return;\n }\n isRedeeming = true;\n clearTimeout(timeout);\n redeem(url, response).then(resolve, reject);\n });\n });\n}\n\nasync function startBrowserSignIn(\n settings: ResolvedDevtoolsOptions,\n publicKey: string\n): Promise<BrowserSignIn> {\n const server = createServer();\n const port = await listenOnLoopback(server);\n const redirectUri = connectCallbackUriFor(`http://${LOOPBACK_HOST}:${port}`);\n const { authorizeUrl, state } = connectAuthorizeUrl(settings, {\n client: \"desktopApp\",\n publicKey,\n redirectUri,\n });\n const account = awaitConnectCallback(server, {\n redirectUri,\n settings,\n state,\n }).finally(() => server.close());\n return { account, authorizeUrl };\n}\n\n/**\n * Runs team tools for a desktop app in its privileged process (Electron's\n * main): sign-in goes through the system browser back to a one-shot loopback\n * listener, and the token stays on disk outside the page, like the dev\n * server's Connect. Expose the returned bridge to the page over IPC and pass it\n * to `<RefletFeedback teamTools={bridge} />`.\n */\nexport function createTeamToolsHost(\n options: TeamToolsHostOptions\n): TeamToolsBridge {\n const settings = resolveDevtoolsOptions(\n {\n apiUrl: options.apiUrl,\n appUrl: options.appUrl,\n root: options.appDataDirectory,\n },\n {}\n );\n let pendingSignIn: Promise<BrowserSignIn> | null = null;\n\n const currentSignIn = (): Promise<BrowserSignIn> => {\n if (pendingSignIn) {\n return pendingSignIn;\n }\n const started = startBrowserSignIn(settings, options.publicKey);\n pendingSignIn = started;\n const release = () => {\n if (pendingSignIn === started) {\n pendingSignIn = null;\n }\n };\n started.then((signIn) => signIn.account).then(release, release);\n return started;\n };\n\n return {\n account: async () => {\n const connection = await readConnection(settings.root, settings.apiUrl);\n return connection && { organizationName: connection.organizationName };\n },\n request: async (call) => {\n const search = call.query ? `?${new URLSearchParams(call.query)}` : \"\";\n const response = await proxyWithBoardCredential(\n new Request(`${settings.apiUrl}/${search}`, {\n body: call.method === \"POST\" ? call.body : undefined,\n method: call.method,\n }),\n call.path,\n settings\n );\n return { body: await response.text(), status: response.status };\n },\n signIn: async () => {\n const signIn = await currentSignIn();\n await options.openExternal(signIn.authorizeUrl);\n return await signIn.account;\n },\n signOut: async () => {\n await forgetConnection(settings);\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,372 @@
1
+ import { d as SelectionOutline } from "./submit-r3_M1ITo.js";
2
+ import { A as sessionSheetPlace, C as AccountFooter, D as MissingRouteHint, E as MISSING_SECRET_KEY_HINT, F as useWorkspaceLoader, H as disconnectBoard, J as reportFailure, M as RecordingLayer, N as SessionWorkspaceProvider, O as SESSIONS_STYLES, S as InboxPanel, T as CONNECT_REQUIREMENT, U as probeDevRoute, V as callReflet, W as startBoardConnect, _ as DevtoolsBarButtons, a as NotePicker, b as CodeTab, c as isActiveNote, d as useNotes, f as findOnPage, g as DevtoolsBar, h as DevtoolsSheet, i as NotesPanel, j as useSessionNavigation, k as SessionsPanel, l as isNoteOnPage, m as DEVTOOLS_STYLES, n as useSheetTab, o as enrichNote, p as usePageHighlight, q as Notices, r as NotePins, s as hasTriedToLocate, u as unsentNoteCount, v as SHEET_TABS, w as AccountNote, x as useCodeLookup, z as devtoolsTransport } from "./use-sheet-tab-CacYFVjI.js";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/devtools/client/account/connect-footer.tsx
6
+ function useBoardConnect(publicKey) {
7
+ const [state, setState] = useState({ kind: "idle" });
8
+ const connect = async () => {
9
+ const tab = window.open("", "_blank");
10
+ if (tab) tab.opener = null;
11
+ setState({ kind: "starting" });
12
+ try {
13
+ const { authorizeUrl } = await startBoardConnect(publicKey);
14
+ if (tab) tab.location.href = authorizeUrl;
15
+ else window.open(authorizeUrl, "_blank", "noopener");
16
+ setState({
17
+ authorizeUrl,
18
+ kind: "waiting"
19
+ });
20
+ } catch (error) {
21
+ tab?.close();
22
+ setState({ kind: "idle" });
23
+ reportFailure("Could not connect to Reflet", error);
24
+ }
25
+ };
26
+ return {
27
+ connect,
28
+ state
29
+ };
30
+ }
31
+ function ConnectFooter({ publicKey }) {
32
+ const { connect, state } = useBoardConnect(publicKey);
33
+ if (state.kind === "waiting") return /* @__PURE__ */ jsxs("div", {
34
+ className: "dt-sheet-account",
35
+ role: "status",
36
+ children: [/* @__PURE__ */ jsx("p", {
37
+ className: "dt-meta",
38
+ children: "Finish connecting in the Reflet tab. This panel updates when you come back."
39
+ }), /* @__PURE__ */ jsxs("div", {
40
+ className: "dt-actions",
41
+ children: [/* @__PURE__ */ jsx("a", {
42
+ className: "dt-btn",
43
+ href: state.authorizeUrl,
44
+ rel: "noopener noreferrer",
45
+ target: "_blank",
46
+ children: "Open Reflet again"
47
+ }), /* @__PURE__ */ jsx("button", {
48
+ className: "dt-btn",
49
+ onClick: connect,
50
+ type: "button",
51
+ children: "Start again"
52
+ })]
53
+ })]
54
+ });
55
+ return /* @__PURE__ */ jsxs("div", {
56
+ className: "dt-sheet-account",
57
+ children: [/* @__PURE__ */ jsx("p", {
58
+ className: "dt-meta",
59
+ children: "Not connected"
60
+ }), /* @__PURE__ */ jsx("button", {
61
+ className: "dt-btn",
62
+ "data-variant": "primary",
63
+ disabled: state.kind === "starting",
64
+ onClick: connect,
65
+ type: "button",
66
+ children: "Connect to Reflet"
67
+ })]
68
+ });
69
+ }
70
+ //#endregion
71
+ //#region src/devtools/client/account/devtools-account.tsx
72
+ async function disconnect(refresh) {
73
+ try {
74
+ const { revoked } = await disconnectBoard();
75
+ if (!revoked) reportFailure("The Reflet token was not revoked", /* @__PURE__ */ new Error("Disconnected here, but Reflet could not be reached. Revoke it from Account → Devtools on reflet.app."));
76
+ } catch (error) {
77
+ reportFailure("The dev server did not disconnect", error);
78
+ }
79
+ refresh();
80
+ }
81
+ function DevtoolsAccount({ connectKey, refresh, route, workspace }) {
82
+ if (route.kind !== "ready") return null;
83
+ const { board } = route.status;
84
+ if (board.kind === "secretKey") return /* @__PURE__ */ jsx(AccountNote, { children: "Reaching the board with REFLET_SECRET_KEY" });
85
+ if (board.kind === "disconnected") return connectKey === null ? /* @__PURE__ */ jsxs(AccountNote, { children: ["Not connected. ", CONNECT_REQUIREMENT] }) : /* @__PURE__ */ jsx(ConnectFooter, { publicKey: connectKey });
86
+ return /* @__PURE__ */ jsx(AccountFooter, {
87
+ actions: [{
88
+ label: "Disconnect",
89
+ onSelect: () => disconnect(refresh),
90
+ variant: "danger"
91
+ }],
92
+ organizationName: board.organizationName,
93
+ person: workspace.kind === "ready" ? workspace.workspace.identity.user : null
94
+ });
95
+ }
96
+ //#endregion
97
+ //#region src/devtools/client/board/board-tab.tsx
98
+ function BoardTab({ canConnect, onOpenCode, onShowSelector, route }) {
99
+ if (route.kind === "missing") return /* @__PURE__ */ jsx(MissingRouteHint, { purpose: "see your board's feedback for this page" });
100
+ if (route.kind === "checking") return null;
101
+ if (route.status.board.kind === "disconnected") return /* @__PURE__ */ jsx("p", {
102
+ className: "dt-empty",
103
+ children: canConnect ? "Connect to Reflet below to see this page's feedback and add notes to your board." : MISSING_SECRET_KEY_HINT
104
+ });
105
+ return /* @__PURE__ */ jsx(InboxPanel, {
106
+ boardCall: callReflet,
107
+ onOpenCode,
108
+ onShowSelector
109
+ });
110
+ }
111
+ //#endregion
112
+ //#region src/devtools/client/route/use-dev-route.ts
113
+ /** Re-probes on window focus so a connection finished in another tab shows up; keeps the last state meanwhile. */
114
+ function useDevRoute() {
115
+ const [route, setRoute] = useState({ kind: "checking" });
116
+ const latestProbe = useRef(0);
117
+ const refresh = useCallback(() => {
118
+ latestProbe.current += 1;
119
+ const probe = latestProbe.current;
120
+ probeDevRoute().then((status) => {
121
+ if (probe === latestProbe.current) setRoute(status ? {
122
+ kind: "ready",
123
+ status
124
+ } : { kind: "missing" });
125
+ });
126
+ }, []);
127
+ useEffect(() => {
128
+ refresh();
129
+ window.addEventListener("focus", refresh);
130
+ return () => {
131
+ window.removeEventListener("focus", refresh);
132
+ latestProbe.current += 1;
133
+ };
134
+ }, [refresh]);
135
+ return {
136
+ refresh,
137
+ route
138
+ };
139
+ }
140
+ //#endregion
141
+ //#region src/devtools/client/sessions/devtools-sessions.tsx
142
+ const DEVTOOLS_TRANSPORT = devtoolsTransport();
143
+ /** Sessions go through the dev route with the developer's own Connect token, never the secret key. */
144
+ function useDevtoolsSessions(options) {
145
+ const { route } = options;
146
+ const isConnected = route.kind === "ready" && route.status.board.kind === "connected";
147
+ const state = useWorkspaceLoader({
148
+ onUnauthorized: options.onUnauthorized,
149
+ surface: "devtools",
150
+ transport: isConnected ? DEVTOOLS_TRANSPORT : null
151
+ });
152
+ return {
153
+ navigation: useSessionNavigation(),
154
+ state
155
+ };
156
+ }
157
+ function SessionsScope({ children, sessions }) {
158
+ const workspace = sessions.state.kind === "ready" ? sessions.state.workspace : null;
159
+ return /* @__PURE__ */ jsx(SessionWorkspaceProvider, {
160
+ workspace,
161
+ children
162
+ });
163
+ }
164
+ const SESSIONS_NEED_ACCOUNT = "Sessions are recorded under your Reflet account.";
165
+ function WorkspaceGate({ actions, sessions }) {
166
+ const { state } = sessions;
167
+ if (state.kind === "ready") return /* @__PURE__ */ jsx(SessionsPanel, {
168
+ navigation: sessions.navigation,
169
+ onStarted: actions.onStarted,
170
+ onWriteNote: actions.onWriteNote
171
+ });
172
+ if (state.kind === "failed") return /* @__PURE__ */ jsxs("div", {
173
+ className: "dt-empty",
174
+ role: "alert",
175
+ children: [/* @__PURE__ */ jsxs("p", { children: ["Sessions did not open: ", state.message] }), /* @__PURE__ */ jsx("button", {
176
+ className: "dt-btn",
177
+ onClick: state.retry,
178
+ type: "button",
179
+ children: "Try again"
180
+ })]
181
+ });
182
+ return /* @__PURE__ */ jsx("p", {
183
+ className: "dt-status",
184
+ children: "Opening your sessions…"
185
+ });
186
+ }
187
+ function SessionsTab({ actions, canConnect, route, sessions }) {
188
+ if (route.kind === "checking") return /* @__PURE__ */ jsx("p", {
189
+ className: "dt-status",
190
+ children: "Connecting to the Reflet dev route…"
191
+ });
192
+ if (route.kind === "missing") return /* @__PURE__ */ jsx(MissingRouteHint, { purpose: "record sessions under your Reflet account" });
193
+ const { board } = route.status;
194
+ if (board.kind === "disconnected") return /* @__PURE__ */ jsxs("p", {
195
+ className: "dt-empty",
196
+ children: [
197
+ SESSIONS_NEED_ACCOUNT,
198
+ " ",
199
+ canConnect ? "Connect to Reflet below to start one." : CONNECT_REQUIREMENT
200
+ ]
201
+ });
202
+ if (board.kind === "secretKey") return /* @__PURE__ */ jsxs("p", {
203
+ className: "dt-empty",
204
+ children: [SESSIONS_NEED_ACCOUNT, " REFLET_SECRET_KEY can't stand for you: remove it from the dev server environment, then connect to Reflet."]
205
+ });
206
+ return /* @__PURE__ */ jsx(WorkspaceGate, {
207
+ actions,
208
+ sessions
209
+ });
210
+ }
211
+ //#endregion
212
+ //#region src/devtools/client/devtools-layer.tsx
213
+ function sendBlockedReason(route, canConnect) {
214
+ if (route.kind === "checking") return "Connecting to the Reflet dev route…";
215
+ if (route.kind === "missing") return "Add the Reflet dev route to add notes to your board.";
216
+ if (route.status.board.kind !== "disconnected") return null;
217
+ return canConnect ? "Connect to Reflet below to add notes to your board." : MISSING_SECRET_KEY_HINT;
218
+ }
219
+ function canConnectBoard(route, publicKey) {
220
+ return route.kind === "ready" && route.status.board.kind === "disconnected" && route.status.board.canConnect && publicKey !== null;
221
+ }
222
+ /** A reload in the middle of locating left these without a source; finish the job once. */
223
+ function useResumeLocating(notes, route) {
224
+ useEffect(() => {
225
+ if (route.kind === "checking") return;
226
+ for (const note of notes.notes) {
227
+ const element = note.source === null && isActiveNote(note) && notes.activity[note.id] === void 0 && !hasTriedToLocate(note.id) && isNoteOnPage(note, window.location.pathname) ? findOnPage(note.selection.selector) : null;
228
+ if (element) enrichNote(note.id, element, route.kind === "ready").catch(() => null);
229
+ }
230
+ }, [notes, route.kind]);
231
+ }
232
+ function SheetPanel({ canConnect, codeLookup, handlers, notes, openTab, route, sessions }) {
233
+ if (openTab === "notes") return /* @__PURE__ */ jsx(NotesPanel, {
234
+ boardCall: callReflet,
235
+ notes,
236
+ onOpenCode: handlers.onOpenCode,
237
+ onPick: handlers.onPick,
238
+ onShowSelector: handlers.onShowSelector,
239
+ sendBlockedReason: sendBlockedReason(route, canConnect)
240
+ });
241
+ if (openTab === "inbox") return /* @__PURE__ */ jsx(BoardTab, {
242
+ canConnect,
243
+ onOpenCode: handlers.onOpenCode,
244
+ onShowSelector: handlers.onShowSelector,
245
+ route
246
+ });
247
+ if (openTab === "sessions") return /* @__PURE__ */ jsx(SessionsTab, {
248
+ actions: {
249
+ onStarted: handlers.onClose,
250
+ onWriteNote: () => {
251
+ handlers.onClose();
252
+ handlers.onPick();
253
+ }
254
+ },
255
+ canConnect,
256
+ route,
257
+ sessions
258
+ });
259
+ return codeLookup && /* @__PURE__ */ jsx(CodeTab, {
260
+ lookup: codeLookup,
261
+ route
262
+ });
263
+ }
264
+ function DevtoolsLayer({ isWidgetOpen, position, publicKey }) {
265
+ const { refresh, route } = useDevRoute();
266
+ const notes = useNotes();
267
+ useResumeLocating(notes, route);
268
+ const sessions = useDevtoolsSessions({
269
+ onUnauthorized: refresh,
270
+ route
271
+ });
272
+ const [isPicking, setIsPicking] = useState(false);
273
+ const sheet = useSheetTab();
274
+ const { openTab } = sheet;
275
+ const { clearHighlight, highlighted, showSelector } = usePageHighlight();
276
+ const { codeLookup, inspectElement, openCode } = useCodeLookup(sheet.select);
277
+ const edge = position.startsWith("top") ? "top" : "bottom";
278
+ const side = position.endsWith("right") ? "right" : "left";
279
+ const canReadSource = route.kind === "ready";
280
+ const canConnect = canConnectBoard(route, publicKey);
281
+ const isRecessed = isWidgetOpen || isPicking;
282
+ const closeSheet = () => {
283
+ sheet.close();
284
+ clearHighlight();
285
+ };
286
+ const startPick = useCallback(() => setIsPicking(true), []);
287
+ const cancelPick = useCallback(() => setIsPicking(false), []);
288
+ const inspect = useMemo(() => ({
289
+ label: "Code",
290
+ onInspect: (element) => {
291
+ setIsPicking(false);
292
+ inspectElement(element);
293
+ }
294
+ }), [inspectElement]);
295
+ const tabs = SHEET_TABS.filter((tab) => tab !== "code" || codeLookup);
296
+ const openPanel = (tab) => sheet.select(tab ?? (tabs.includes(sheet.lastTab) ? sheet.lastTab : "inbox"));
297
+ const sheetPlace = openTab === "sessions" && sessions.state.kind === "ready" ? sessionSheetPlace(sessions.navigation) : null;
298
+ return /* @__PURE__ */ jsx(SessionsScope, {
299
+ sessions,
300
+ children: /* @__PURE__ */ jsxs("div", {
301
+ className: "dt",
302
+ "data-recessed": isRecessed,
303
+ "data-sheet-open": openTab !== null,
304
+ children: [
305
+ /* @__PURE__ */ jsx("style", { children: `${DEVTOOLS_STYLES}${SESSIONS_STYLES}` }),
306
+ /* @__PURE__ */ jsx(NotePins, {
307
+ isVisible: !isRecessed,
308
+ notes,
309
+ onOpenCode: openCode
310
+ }),
311
+ openTab && /* @__PURE__ */ jsx(DevtoolsSheet, {
312
+ footer: sheetPlace === null && /* @__PURE__ */ jsx(DevtoolsAccount, {
313
+ connectKey: canConnect ? publicKey : null,
314
+ refresh,
315
+ route,
316
+ workspace: sessions.state
317
+ }),
318
+ navigation: {
319
+ onSelectTab: sheet.select,
320
+ place: sheetPlace,
321
+ tab: openTab,
322
+ tabs
323
+ },
324
+ onClose: closeSheet,
325
+ side,
326
+ children: /* @__PURE__ */ jsx(SheetPanel, {
327
+ canConnect,
328
+ codeLookup,
329
+ handlers: {
330
+ onClose: closeSheet,
331
+ onOpenCode: openCode,
332
+ onPick: startPick,
333
+ onShowSelector: showSelector
334
+ },
335
+ notes,
336
+ openTab,
337
+ route,
338
+ sessions
339
+ })
340
+ }),
341
+ /* @__PURE__ */ jsx(DevtoolsBar, {
342
+ edge,
343
+ side,
344
+ children: /* @__PURE__ */ jsx(DevtoolsBarButtons, {
345
+ canOpenCode: true,
346
+ onOpen: openPanel,
347
+ onPick: startPick,
348
+ unsentNoteCount: unsentNoteCount(notes)
349
+ })
350
+ }),
351
+ openTab && /* @__PURE__ */ jsx(SelectionOutline, { node: highlighted }),
352
+ /* @__PURE__ */ jsx(Notices, {}),
353
+ sessions.state.kind === "ready" && /* @__PURE__ */ jsx(RecordingLayer, { onFinished: (target) => {
354
+ sessions.navigation.go({
355
+ target,
356
+ view: "review"
357
+ });
358
+ sheet.select("sessions");
359
+ } }),
360
+ isPicking && /* @__PURE__ */ jsx(NotePicker, {
361
+ canReadSource,
362
+ inspect,
363
+ onDone: cancelPick
364
+ })
365
+ ]
366
+ })
367
+ });
368
+ }
369
+ //#endregion
370
+ export { DevtoolsLayer };
371
+
372
+ //# sourceMappingURL=devtools-layer-CYIoe3Vo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devtools-layer-CYIoe3Vo.js","names":[],"sources":["../src/devtools/client/account/connect-footer.tsx","../src/devtools/client/account/devtools-account.tsx","../src/devtools/client/board/board-tab.tsx","../src/devtools/client/route/use-dev-route.ts","../src/devtools/client/sessions/devtools-sessions.tsx","../src/devtools/client/devtools-layer.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { reportFailure } from \"../notices\";\nimport { startBoardConnect } from \"../route/dev-route\";\n\ntype ConnectState =\n | { kind: \"idle\" }\n | { kind: \"starting\" }\n | { authorizeUrl: string; kind: \"waiting\" };\n\nfunction useBoardConnect(publicKey: string) {\n const [state, setState] = useState<ConnectState>({ kind: \"idle\" });\n\n const connect = async () => {\n // Opened before any await: browsers only allow popups inside the click.\n const tab = window.open(\"\", \"_blank\");\n if (tab) {\n tab.opener = null;\n }\n setState({ kind: \"starting\" });\n try {\n const { authorizeUrl } = await startBoardConnect(publicKey);\n if (tab) {\n tab.location.href = authorizeUrl;\n } else {\n window.open(authorizeUrl, \"_blank\", \"noopener\");\n }\n setState({ authorizeUrl, kind: \"waiting\" });\n } catch (error) {\n tab?.close();\n setState({ kind: \"idle\" });\n reportFailure(\"Could not connect to Reflet\", error);\n }\n };\n return { connect, state };\n}\n\nexport function ConnectFooter({ publicKey }: { publicKey: string }) {\n const { connect, state } = useBoardConnect(publicKey);\n\n if (state.kind === \"waiting\") {\n return (\n <div className=\"dt-sheet-account\" role=\"status\">\n <p className=\"dt-meta\">\n Finish connecting in the Reflet tab. This panel updates when you come\n back.\n </p>\n <div className=\"dt-actions\">\n <a\n className=\"dt-btn\"\n href={state.authorizeUrl}\n rel=\"noopener noreferrer\"\n target=\"_blank\"\n >\n Open Reflet again\n </a>\n <button className=\"dt-btn\" onClick={connect} type=\"button\">\n Start again\n </button>\n </div>\n </div>\n );\n }\n return (\n <div className=\"dt-sheet-account\">\n <p className=\"dt-meta\">Not connected</p>\n <button\n className=\"dt-btn\"\n data-variant=\"primary\"\n disabled={state.kind === \"starting\"}\n onClick={connect}\n type=\"button\"\n >\n Connect to Reflet\n </button>\n </div>\n );\n}\n","import type { WorkspaceState } from \"../../../sessions/ui/session-workspace\";\nimport { reportFailure } from \"../notices\";\nimport { disconnectBoard } from \"../route/dev-route\";\nimport { CONNECT_REQUIREMENT } from \"../route/route-hint\";\nimport type { DevRouteState } from \"../route/use-dev-route\";\nimport { AccountFooter, AccountNote } from \"./account-footer\";\nimport { ConnectFooter } from \"./connect-footer\";\n\nasync function disconnect(refresh: () => void) {\n try {\n const { revoked } = await disconnectBoard();\n if (!revoked) {\n reportFailure(\n \"The Reflet token was not revoked\",\n new Error(\n \"Disconnected here, but Reflet could not be reached. Revoke it from Account → Devtools on reflet.app.\"\n )\n );\n }\n } catch (error) {\n reportFailure(\"The dev server did not disconnect\", error);\n }\n refresh();\n}\n\nexport function DevtoolsAccount({\n connectKey,\n refresh,\n route,\n workspace,\n}: {\n connectKey: string | null;\n refresh: () => void;\n route: DevRouteState;\n workspace: WorkspaceState;\n}) {\n if (route.kind !== \"ready\") {\n return null;\n }\n const { board } = route.status;\n if (board.kind === \"secretKey\") {\n return <AccountNote>Reaching the board with REFLET_SECRET_KEY</AccountNote>;\n }\n if (board.kind === \"disconnected\") {\n return connectKey === null ? (\n <AccountNote>Not connected. {CONNECT_REQUIREMENT}</AccountNote>\n ) : (\n <ConnectFooter publicKey={connectKey} />\n );\n }\n return (\n <AccountFooter\n actions={[\n {\n label: \"Disconnect\",\n onSelect: () => disconnect(refresh),\n variant: \"danger\",\n },\n ]}\n organizationName={board.organizationName}\n person={\n workspace.kind === \"ready\" ? workspace.workspace.identity.user : null\n }\n />\n );\n}\n","import type { CodeTarget } from \"../code/code-panel\";\nimport { InboxPanel } from \"../inbox/inbox-panel\";\nimport { callReflet } from \"../route/dev-route\";\nimport { MISSING_SECRET_KEY_HINT, MissingRouteHint } from \"../route/route-hint\";\nimport type { DevRouteState } from \"../route/use-dev-route\";\n\nexport function BoardTab({\n canConnect,\n onOpenCode,\n onShowSelector,\n route,\n}: {\n canConnect: boolean;\n onOpenCode: (target: CodeTarget) => void;\n onShowSelector: (selector: string) => boolean;\n route: DevRouteState;\n}) {\n if (route.kind === \"missing\") {\n return (\n <MissingRouteHint purpose=\"see your board's feedback for this page\" />\n );\n }\n if (route.kind === \"checking\") {\n return null;\n }\n if (route.status.board.kind === \"disconnected\") {\n return (\n <p className=\"dt-empty\">\n {canConnect\n ? \"Connect to Reflet below to see this page's feedback and add notes to your board.\"\n : MISSING_SECRET_KEY_HINT}\n </p>\n );\n }\n return (\n <InboxPanel\n boardCall={callReflet}\n onOpenCode={onOpenCode}\n onShowSelector={onShowSelector}\n />\n );\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { DevtoolsStatus } from \"../../protocol\";\nimport { probeDevRoute } from \"./dev-route\";\n\nexport type DevRouteState =\n | { kind: \"checking\" }\n | { kind: \"missing\" }\n | { kind: \"ready\"; status: DevtoolsStatus };\n\n/** Re-probes on window focus so a connection finished in another tab shows up; keeps the last state meanwhile. */\nexport function useDevRoute(): { refresh: () => void; route: DevRouteState } {\n const [route, setRoute] = useState<DevRouteState>({ kind: \"checking\" });\n const latestProbe = useRef(0);\n\n const refresh = useCallback(() => {\n latestProbe.current += 1;\n const probe = latestProbe.current;\n probeDevRoute().then((status) => {\n if (probe === latestProbe.current) {\n setRoute(status ? { kind: \"ready\", status } : { kind: \"missing\" });\n }\n });\n }, []);\n\n useEffect(() => {\n refresh();\n window.addEventListener(\"focus\", refresh);\n return () => {\n window.removeEventListener(\"focus\", refresh);\n latestProbe.current += 1;\n };\n }, [refresh]);\n\n return { refresh, route };\n}\n","import type { ReactNode } from \"react\";\nimport { devtoolsTransport } from \"../../../sessions/api/session-transports\";\nimport {\n SessionWorkspaceProvider,\n useWorkspaceLoader,\n type WorkspaceState,\n} from \"../../../sessions/ui/session-workspace\";\nimport {\n type SessionNavigation,\n SessionsPanel,\n useSessionNavigation,\n} from \"../../../sessions/ui/sessions-panel\";\nimport { CONNECT_REQUIREMENT, MissingRouteHint } from \"../route/route-hint\";\nimport type { DevRouteState } from \"../route/use-dev-route\";\n\nconst DEVTOOLS_TRANSPORT = devtoolsTransport();\n\nexport interface DevtoolsSessions {\n navigation: SessionNavigation;\n state: WorkspaceState;\n}\n\n/** Sessions go through the dev route with the developer's own Connect token, never the secret key. */\nexport function useDevtoolsSessions(options: {\n onUnauthorized: () => void;\n route: DevRouteState;\n}): DevtoolsSessions {\n const { route } = options;\n const isConnected =\n route.kind === \"ready\" && route.status.board.kind === \"connected\";\n const state = useWorkspaceLoader({\n onUnauthorized: options.onUnauthorized,\n surface: \"devtools\",\n transport: isConnected ? DEVTOOLS_TRANSPORT : null,\n });\n return { navigation: useSessionNavigation(), state };\n}\n\nexport function SessionsScope({\n children,\n sessions,\n}: {\n children: ReactNode;\n sessions: DevtoolsSessions;\n}) {\n const workspace =\n sessions.state.kind === \"ready\" ? sessions.state.workspace : null;\n return (\n <SessionWorkspaceProvider workspace={workspace}>\n {children}\n </SessionWorkspaceProvider>\n );\n}\n\nconst SESSIONS_NEED_ACCOUNT =\n \"Sessions are recorded under your Reflet account.\";\n\nfunction WorkspaceGate({\n actions,\n sessions,\n}: {\n actions: SessionsTabActions;\n sessions: DevtoolsSessions;\n}) {\n const { state } = sessions;\n if (state.kind === \"ready\") {\n return (\n <SessionsPanel\n navigation={sessions.navigation}\n onStarted={actions.onStarted}\n onWriteNote={actions.onWriteNote}\n />\n );\n }\n if (state.kind === \"failed\") {\n return (\n <div className=\"dt-empty\" role=\"alert\">\n <p>Sessions did not open: {state.message}</p>\n <button className=\"dt-btn\" onClick={state.retry} type=\"button\">\n Try again\n </button>\n </div>\n );\n }\n return <p className=\"dt-status\">Opening your sessions…</p>;\n}\n\nexport interface SessionsTabActions {\n onStarted: () => void;\n onWriteNote?: () => void;\n}\n\nexport function SessionsTab({\n actions,\n canConnect,\n route,\n sessions,\n}: {\n actions: SessionsTabActions;\n canConnect: boolean;\n route: DevRouteState;\n sessions: DevtoolsSessions;\n}) {\n if (route.kind === \"checking\") {\n return <p className=\"dt-status\">Connecting to the Reflet dev route…</p>;\n }\n if (route.kind === \"missing\") {\n return (\n <MissingRouteHint purpose=\"record sessions under your Reflet account\" />\n );\n }\n const { board } = route.status;\n if (board.kind === \"disconnected\") {\n return (\n <p className=\"dt-empty\">\n {SESSIONS_NEED_ACCOUNT}{\" \"}\n {canConnect\n ? \"Connect to Reflet below to start one.\"\n : CONNECT_REQUIREMENT}\n </p>\n );\n }\n if (board.kind === \"secretKey\") {\n return (\n <p className=\"dt-empty\">\n {SESSIONS_NEED_ACCOUNT} REFLET_SECRET_KEY can't stand for you: remove it\n from the dev server environment, then connect to Reflet.\n </p>\n );\n }\n return <WorkspaceGate actions={actions} sessions={sessions} />;\n}\n","import { useCallback, useEffect, useMemo, useState } from \"react\";\nimport type { RefletFeedbackProps } from \"../../feedback/types\";\nimport { SelectionOutline } from \"../../feedback/ui/selection-outline\";\nimport { RecordingLayer } from \"../../sessions/ui/recording/recording-layer\";\nimport { sessionSheetPlace } from \"../../sessions/ui/sessions-panel\";\nimport { SESSIONS_STYLES } from \"../../sessions/ui/sessions-styles\";\nimport { DevtoolsAccount } from \"./account/devtools-account\";\nimport { BoardTab } from \"./board/board-tab\";\nimport type { CodeTarget } from \"./code/code-panel\";\nimport { type CodeLookup, CodeTab, useCodeLookup } from \"./code/code-tab\";\nimport {\n DevtoolsBar,\n DevtoolsBarButtons,\n SHEET_TABS,\n type SheetTab,\n} from \"./devtools-bar\";\nimport { DevtoolsSheet } from \"./devtools-sheet\";\nimport { DEVTOOLS_STYLES } from \"./devtools-styles\";\nimport { findOnPage, usePageHighlight } from \"./find-on-page\";\nimport { enrichNote, hasTriedToLocate } from \"./notes/capture-note\";\nimport { NotePicker } from \"./notes/note-picker\";\nimport {\n isActiveNote,\n isNoteOnPage,\n type NotesSnapshot,\n unsentNoteCount,\n useNotes,\n} from \"./notes/note-store\";\nimport { NotesPanel } from \"./notes/notes-panel\";\nimport { Notices } from \"./notices\";\nimport { NotePins } from \"./pins/note-pins\";\nimport { callReflet } from \"./route/dev-route\";\nimport { MISSING_SECRET_KEY_HINT } from \"./route/route-hint\";\nimport { type DevRouteState, useDevRoute } from \"./route/use-dev-route\";\nimport {\n type DevtoolsSessions,\n SessionsScope,\n SessionsTab,\n useDevtoolsSessions,\n} from \"./sessions/devtools-sessions\";\nimport { FIRST_SHEET_TAB, useSheetTab } from \"./use-sheet-tab\";\n\nfunction sendBlockedReason(\n route: DevRouteState,\n canConnect: boolean\n): string | null {\n if (route.kind === \"checking\") {\n return \"Connecting to the Reflet dev route…\";\n }\n if (route.kind === \"missing\") {\n return \"Add the Reflet dev route to add notes to your board.\";\n }\n if (route.status.board.kind !== \"disconnected\") {\n return null;\n }\n return canConnect\n ? \"Connect to Reflet below to add notes to your board.\"\n : MISSING_SECRET_KEY_HINT;\n}\nfunction canConnectBoard(route: DevRouteState, publicKey: string | null) {\n return (\n route.kind === \"ready\" &&\n route.status.board.kind === \"disconnected\" &&\n route.status.board.canConnect &&\n publicKey !== null\n );\n}\n\n/** A reload in the middle of locating left these without a source; finish the job once. */\nfunction useResumeLocating(notes: NotesSnapshot, route: DevRouteState) {\n useEffect(() => {\n if (route.kind === \"checking\") {\n return;\n }\n for (const note of notes.notes) {\n const isUnlocated =\n note.source === null &&\n isActiveNote(note) &&\n notes.activity[note.id] === undefined &&\n !hasTriedToLocate(note.id) &&\n isNoteOnPage(note, window.location.pathname);\n const element = isUnlocated ? findOnPage(note.selection.selector) : null;\n if (element) {\n enrichNote(note.id, element, route.kind === \"ready\").catch(() => null);\n }\n }\n }, [notes, route.kind]);\n}\n\nfunction SheetPanel({\n canConnect,\n codeLookup,\n handlers,\n notes,\n openTab,\n route,\n sessions,\n}: {\n canConnect: boolean;\n codeLookup: CodeLookup | null;\n handlers: {\n onClose: () => void;\n onOpenCode: (target: CodeTarget) => void;\n onPick: () => void;\n onShowSelector: (selector: string) => boolean;\n };\n notes: NotesSnapshot;\n openTab: SheetTab;\n route: DevRouteState;\n sessions: DevtoolsSessions;\n}) {\n if (openTab === \"notes\") {\n return (\n <NotesPanel\n boardCall={callReflet}\n notes={notes}\n onOpenCode={handlers.onOpenCode}\n onPick={handlers.onPick}\n onShowSelector={handlers.onShowSelector}\n sendBlockedReason={sendBlockedReason(route, canConnect)}\n />\n );\n }\n if (openTab === \"inbox\") {\n return (\n <BoardTab\n canConnect={canConnect}\n onOpenCode={handlers.onOpenCode}\n onShowSelector={handlers.onShowSelector}\n route={route}\n />\n );\n }\n if (openTab === \"sessions\") {\n return (\n <SessionsTab\n actions={{\n onStarted: handlers.onClose,\n onWriteNote: () => {\n handlers.onClose();\n handlers.onPick();\n },\n }}\n canConnect={canConnect}\n route={route}\n sessions={sessions}\n />\n );\n }\n return codeLookup && <CodeTab lookup={codeLookup} route={route} />;\n}\n\nexport function DevtoolsLayer({\n isWidgetOpen,\n position,\n publicKey,\n}: {\n isWidgetOpen: boolean;\n position: NonNullable<RefletFeedbackProps[\"position\"]>;\n publicKey: string | null;\n}) {\n const { refresh, route } = useDevRoute();\n const notes = useNotes();\n useResumeLocating(notes, route);\n const sessions = useDevtoolsSessions({ onUnauthorized: refresh, route });\n const [isPicking, setIsPicking] = useState(false);\n const sheet = useSheetTab();\n const { openTab } = sheet;\n const { clearHighlight, highlighted, showSelector } = usePageHighlight();\n const { codeLookup, inspectElement, openCode } = useCodeLookup(sheet.select);\n\n const edge = position.startsWith(\"top\") ? \"top\" : \"bottom\";\n const side = position.endsWith(\"right\") ? \"right\" : \"left\";\n const canReadSource = route.kind === \"ready\";\n const canConnect = canConnectBoard(route, publicKey);\n const isRecessed = isWidgetOpen || isPicking;\n\n const closeSheet = () => {\n sheet.close();\n clearHighlight();\n };\n\n const startPick = useCallback(() => setIsPicking(true), []);\n const cancelPick = useCallback(() => setIsPicking(false), []);\n\n const inspect = useMemo(\n () => ({\n label: \"Code\",\n onInspect: (element: Element) => {\n setIsPicking(false);\n inspectElement(element);\n },\n }),\n [inspectElement]\n );\n\n const tabs = SHEET_TABS.filter((tab) => tab !== \"code\" || codeLookup);\n const openPanel = (tab: SheetTab | null) =>\n sheet.select(\n tab ?? (tabs.includes(sheet.lastTab) ? sheet.lastTab : FIRST_SHEET_TAB)\n );\n const sheetPlace =\n openTab === \"sessions\" && sessions.state.kind === \"ready\"\n ? sessionSheetPlace(sessions.navigation)\n : null;\n\n return (\n <SessionsScope sessions={sessions}>\n <div\n className=\"dt\"\n data-recessed={isRecessed}\n data-sheet-open={openTab !== null}\n >\n <style>{`${DEVTOOLS_STYLES}${SESSIONS_STYLES}`}</style>\n <NotePins isVisible={!isRecessed} notes={notes} onOpenCode={openCode} />\n {openTab && (\n <DevtoolsSheet\n footer={\n sheetPlace === null && (\n <DevtoolsAccount\n connectKey={canConnect ? publicKey : null}\n refresh={refresh}\n route={route}\n workspace={sessions.state}\n />\n )\n }\n navigation={{\n onSelectTab: sheet.select,\n place: sheetPlace,\n tab: openTab,\n tabs,\n }}\n onClose={closeSheet}\n side={side}\n >\n <SheetPanel\n canConnect={canConnect}\n codeLookup={codeLookup}\n handlers={{\n onClose: closeSheet,\n onOpenCode: openCode,\n onPick: startPick,\n onShowSelector: showSelector,\n }}\n notes={notes}\n openTab={openTab}\n route={route}\n sessions={sessions}\n />\n </DevtoolsSheet>\n )}\n <DevtoolsBar edge={edge} side={side}>\n <DevtoolsBarButtons\n canOpenCode\n onOpen={openPanel}\n onPick={startPick}\n unsentNoteCount={unsentNoteCount(notes)}\n />\n </DevtoolsBar>\n {openTab && <SelectionOutline node={highlighted} />}\n <Notices />\n {sessions.state.kind === \"ready\" && (\n <RecordingLayer\n onFinished={(target) => {\n sessions.navigation.go({ target, view: \"review\" });\n sheet.select(\"sessions\");\n }}\n />\n )}\n {isPicking && (\n <NotePicker\n canReadSource={canReadSource}\n inspect={inspect}\n onDone={cancelPick}\n />\n )}\n </div>\n </SessionsScope>\n );\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { t as createDevtoolsHandler } from "./handler-ndF3gZBx.js";
1
+ import { t as createDevtoolsHandler } from "./handler-1XhfZvZ0.js";
2
2
  //#region src/devtools/server/next.ts
3
3
  /**
4
4
  * Route handlers for `app/api/reflet-devtools/[...path]/route.ts`. They answer
@@ -1,6 +1,6 @@
1
- import { r as DEVTOOLS_ROUTE_BASE } from "./protocol-cJVVwnPe.js";
2
- import { h as isLoopbackAddress, v as errorResponse } from "./reflet-proxy-BW6q9xds.js";
3
- import { t as createDevtoolsHandler } from "./handler-ndF3gZBx.js";
1
+ import { r as DEVTOOLS_ROUTE_BASE } from "./protocol-BnurX0CF.js";
2
+ import { h as isLoopbackAddress, v as errorResponse } from "./reflet-proxy-DVE6ov2f.js";
3
+ import { t as createDevtoolsHandler } from "./handler-1XhfZvZ0.js";
4
4
  import { loadEnv } from "vite";
5
5
  //#region src/devtools/server/vite.ts
6
6
  const MAX_BODY_BYTES = 1048576;
package/dist/feedback.js CHANGED
@@ -1,9 +1,8 @@
1
1
  "use client";
2
2
  import { n as Reflet } from "./client-oFFfRxAM.js";
3
- import { A as SpotlightIcon, B as SDK_VERSION, C as ChevronDownIcon, D as HighlightIcon, E as GripIcon, F as TrashIcon, H as collectPageContext, I as UndoIcon, L as isDegenerate, N as TargetIcon, O as MoreIcon, P as TextIcon, R as normalizeRect, S as CheckIcon, U as MAX_SELECTION_COMMENT_LENGTH, W as buildElementSelection, Y as maskSecrets, _ as matchesHotkey, at as releaseCapture, b as CameraIcon, c as useFloatingPosition, d as drawAnnotations, f as listenOnPageAndWidget, g as isSubmitEnter, h as stopClicksAtHost, i as submitWidgetFeedback, it as capturedViewportSize, j as SquareIcon, k as PencilIcon, n as createFeedbackTransport, nt as captureViewport, o as ElementPicker, p as listenToKeydown, t as attachPreparedScreenshots, tt as WIDGET_MARKER, v as ArrowIcon, w as CloseIcon, x as ChatIcon, y as BlurIcon, z as DEFAULT_WIDGET_LABELS } from "./submit-DsJwTy3X.js";
3
+ import { A as PencilIcon, B as DEFAULT_WIDGET_LABELS, C as CheckIcon, D as GripIcon, F as TextIcon, G as buildElementSelection, I as TrashIcon, L as UndoIcon, M as SquareIcon, O as HighlightIcon, P as TargetIcon, R as isDegenerate, S as ChatIcon, T as CloseIcon, U as collectPageContext, V as SDK_VERSION, W as MAX_SELECTION_COMMENT_LENGTH, X as maskSecrets, _ as isSubmitEnter, at as capturedViewportSize, b as BlurIcon, c as useFloatingPosition, d as SelectionOutline, f as drawAnnotations, g as stopClicksAtHost, i as submitWidgetFeedback, j as SpotlightIcon, k as MoreIcon, m as listenToKeydown, n as createFeedbackTransport, nt as WIDGET_MARKER, o as ElementPicker, ot as releaseCapture, p as listenOnPageAndWidget, rt as captureViewport, t as attachPreparedScreenshots, v as matchesHotkey, w as ChevronDownIcon, x as CameraIcon, y as ArrowIcon, z as normalizeRect } from "./submit-r3_M1ITo.js";
4
4
  import { t as RefletContext } from "./react-context-Kazo_JCz.js";
5
5
  import { n as hasTeamActivation } from "./team-activation-Cv5U7OF1.js";
6
- import { t as SelectionOutline } from "./selection-outline-NzAAAZXR.js";
7
6
  import { t as REFLET_Z_INDEX } from "./z-index-CwokqVq_.js";
8
7
  import { Suspense, lazy, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
9
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -2304,7 +2303,7 @@ function useWidgetState(props) {
2304
2303
  setStep("compose");
2305
2304
  return submission.submit({
2306
2305
  context: {
2307
- ...activeScreenshot?.context ?? collectPageContext({ sdkVersion: "0.7.6" }),
2306
+ ...activeScreenshot?.context ?? collectPageContext({ sdkVersion: "0.7.7" }),
2308
2307
  consoleEvents: consoleRef.current?.events(),
2309
2308
  metadata: props.metadata,
2310
2309
  selections
@@ -2370,8 +2369,8 @@ function useWidgetState(props) {
2370
2369
  }
2371
2370
  //#endregion
2372
2371
  //#region src/feedback/widget.tsx
2373
- const DevtoolsLayer = process.env.NODE_ENV === "development" ? lazy(() => import("./devtools-layer-xN47FKpW.js").then((module) => ({ default: module.DevtoolsLayer }))) : null;
2374
- const TeamLayer = lazy(() => import("./team-layer-VlQtvR5O.js").then((module) => ({ default: module.TeamLayer })));
2372
+ const DevtoolsLayer = process.env.NODE_ENV === "development" ? lazy(() => import("./devtools-layer-CYIoe3Vo.js").then((module) => ({ default: module.DevtoolsLayer }))) : null;
2373
+ const TeamLayer = lazy(() => import("./team-layer-5-uPYTjV.js").then((module) => ({ default: module.TeamLayer })));
2375
2374
  const DEFAULT_APP_URL = "https://www.reflet.app";
2376
2375
  const ignoreChanges = () => () => void 0;
2377
2376
  /** Activation reads the URL and storage, which only exist in the browser: the server renders without team tools. */