reflet-sdk 0.7.6 → 0.7.8
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/CHANGELOG.md +34 -0
- package/README.md +15 -6
- package/dist/desktop.d.ts.map +1 -1
- package/dist/desktop.js +3 -2
- package/dist/desktop.js.map +1 -1
- package/dist/devtools-layer-B_b_3MH3.js +372 -0
- package/dist/devtools-layer-B_b_3MH3.js.map +1 -0
- package/dist/devtools-next.js +1 -1
- package/dist/devtools-vite.js +3 -3
- package/dist/feedback.js +4 -5
- package/dist/feedback.js.map +1 -1
- package/dist/{handler-ndF3gZBx.js → handler-D8c3woEz.js} +3 -3
- package/dist/{handler-ndF3gZBx.js.map → handler-D8c3woEz.js.map} +1 -1
- package/dist/{protocol-cJVVwnPe.js → protocol-ClxYWmhu.js} +2 -2
- package/dist/{protocol-cJVVwnPe.js.map → protocol-ClxYWmhu.js.map} +1 -1
- package/dist/{reflet-proxy-BW6q9xds.js → reflet-proxy-Dg6mea-4.js} +14 -7
- package/dist/reflet-proxy-Dg6mea-4.js.map +1 -0
- package/dist/{submit-DsJwTy3X.js → submit-D_DrRBeB.js} +49 -3
- package/dist/submit-D_DrRBeB.js.map +1 -0
- package/dist/{team-layer-VlQtvR5O.js → team-layer-CxMBo9WN.js} +162 -33
- package/dist/team-layer-CxMBo9WN.js.map +1 -0
- package/dist/{devtools-styles-DQpiubGB.js → use-sheet-tab-azUezLAq.js} +5261 -1573
- package/dist/use-sheet-tab-azUezLAq.js.map +1 -0
- package/package.json +1 -1
- package/dist/devtools-layer-xN47FKpW.js +0 -3818
- package/dist/devtools-layer-xN47FKpW.js.map +0 -1
- package/dist/devtools-styles-DQpiubGB.js.map +0 -1
- package/dist/reflet-proxy-BW6q9xds.js.map +0 -1
- package/dist/selection-outline-NzAAAZXR.js +0 -51
- package/dist/selection-outline-NzAAAZXR.js.map +0 -1
- package/dist/submit-DsJwTy3X.js.map +0 -1
- package/dist/team-layer-VlQtvR5O.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,40 @@ 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.8] - 2026-10-17
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Sessions: when several intentions are undecided, Confirm all confirms them in one go, with a single Undo.
|
|
13
|
+
- Sessions: an × on the recording bar cancels the recording after you confirm. Nothing of it is kept: what was not uploaded yet is dropped, and a session already created on Reflet is deleted, even if you were offline when you cancelled.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Sessions: the review header fits on one line: a smaller title, then the date, a clock with the duration, and a lock or team icon for who can see it.
|
|
18
|
+
- Sessions: while a session has no intention yet, its processing steps sit in the middle of the Intentions tab. Uploading and finding intentions show a moving bar; transcription shows a bar that fills as passages are transcribed.
|
|
19
|
+
- Sessions: the Evidence tab is now Screenshots. Its screenshots keep their proportions at full width, each with a clock icon and its time above it. Clicking the card shows the moment; the trash icon to remove it appears on hover. Enlarge is gone.
|
|
20
|
+
- Sessions: a transcript row or an intention's quoted passage shows the moment when clicked, instead of a Show context link. Edit is a pencil that appears on hover.
|
|
21
|
+
- Sessions: Prepare brief and Add to board are always shown, side by side at full width. When they cannot act yet, they are disabled and say why on hover, instead of a sentence under them.
|
|
22
|
+
- Sessions: clicking a pressed Confirm, Later or Dismiss puts the intention back to undecided. Labels on screenshots wrap onto several lines without breaking their rounded shape. A screenshot's page is only named when it changes, and a desktop app's `file:` path is no longer shown as the page.
|
|
23
|
+
- Sessions: the recording bar's Mark moment, Point at element, Pause and Resume are icon buttons with tooltips. It no longer says "Saved" or how many uploads are waiting, nor "Paused · nothing is recorded"; it only speaks up when you are offline or nothing can be saved on this device. Pausing and resuming no longer change its width.
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
|
|
27
|
+
- Sessions: Add an intention. Intentions come from the session itself; anything else goes in a note.
|
|
28
|
+
|
|
29
|
+
## [0.7.7] - 2026-10-17
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- 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.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- Team tools: the bar's grip tooltip names the organization you are signed in to.
|
|
38
|
+
- 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.
|
|
39
|
+
- 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.
|
|
40
|
+
- 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".
|
|
41
|
+
|
|
8
42
|
## [0.7.6] - 2026-10-17
|
|
9
43
|
|
|
10
44
|
### Changed
|
package/README.md
CHANGED
|
@@ -215,12 +215,15 @@ 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
|
|
219
|
-
proposes the intentions behind what you said
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
|
221
|
+
one, or confirm all undecided ones at once. Like notes, confirmed intentions
|
|
222
|
+
have two ways out: **Prepare brief**
|
|
223
|
+
shows the brief right away, ready to copy into a coding agent or download as
|
|
224
|
+
a `.zip` with its screenshots (nothing is shared until you copy or download
|
|
225
|
+
it), and **Add to board** turns them into internal feedback, one by one or
|
|
226
|
+
all at once.
|
|
224
227
|
Sessions are recorded under your own name, so they need Connect rather than
|
|
225
228
|
`REFLET_SECRET_KEY`.
|
|
226
229
|
|
|
@@ -330,6 +333,12 @@ Expose the four calls from the preload script, then pass them as the bridge:
|
|
|
330
333
|
at once, so only pass the bridge in builds meant for your team. `request` only
|
|
331
334
|
forwards the session and feedback endpoints the devtools use.
|
|
332
335
|
|
|
336
|
+
A desktop app gets more than sessions: the bar opens the devtools panel with
|
|
337
|
+
Board, Notes and Sessions, so members can pick an element, note it, add it to
|
|
338
|
+
the board, and read the board's feedback. Everything that needs your source
|
|
339
|
+
code (the Code tab, source locations you can open) stays in development. The
|
|
340
|
+
browser flow above records sessions only.
|
|
341
|
+
|
|
333
342
|
### 5. In-App Surveys
|
|
334
343
|
|
|
335
344
|
Create surveys in the Reflet dashboard (NPS, ratings, choices, text, statements,
|
package/dist/desktop.d.ts.map
CHANGED
|
@@ -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;;;;;;;;;
|
|
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-
|
|
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-Dg6mea-4.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
|
});
|
package/dist/desktop.js.map
CHANGED
|
@@ -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":";;;AA6CA,MAAM,gBAAgB;AACtB,MAAM,oBAAoB;AAE1B,eAAe,SAAS,UAA0B,MAAgB;CAChE,SAAS,UAAU,KAAK,QAAQ,OAAO,YAAY,KAAK,OAAO,CAAC;CAChE,SAAS,IAAI,MAAM,KAAK,KAAK,CAAC;AAChC;AAEA,eAAe,iBAAiB,QAAiC;CAC/D,MAAM,IAAI,SAAe,SAAS,WAAW;EAC3C,OAAO,KAAK,SAAS,MAAM;EAC3B,OAAO,OAAO,GAAG,eAAe,OAAO;CACzC,CAAC;CACD,MAAM,UAAU,OAAO,QAAQ;CAC/B,IAAI,YAAY,QAAQ,OAAO,YAAY,UACzC,MAAM,IAAI,MAAM,0CAA0C;CAE5D,OAAO,QAAQ;AACjB;;AAGA,SAAS,qBACP,QACA,SAK2B;CAC3B,MAAM,eAAe,IAAI,IAAI,QAAQ,WAAW,CAAC,CAAC;CAClD,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,UAAU,iBACR,uBAAO,IAAI,MAAM,iBAAiB,CAAC,GACzC,sBACF;EACA,IAAI,cAAc;EAClB,MAAM,SAAS,OAAO,KAAU,aAA6B;GAC3D,MAAM,UAAU,MAAM,kBAAkB,QAAQ,UAAU;IACxD,MAAM,IAAI,aAAa,IAAI,MAAM;IACjC,aAAa,QAAQ;IACrB,OAAO,QAAQ;GACjB,CAAC;GACD,MAAM,SAAS,UAAU,mBAAmB,
|
|
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-D_DrRBeB.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-azUezLAq.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-B_b_3MH3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devtools-layer-B_b_3MH3.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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"}
|
package/dist/devtools-next.js
CHANGED
package/dist/devtools-vite.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { r as DEVTOOLS_ROUTE_BASE } from "./protocol-
|
|
2
|
-
import { h as isLoopbackAddress, v as errorResponse } from "./reflet-proxy-
|
|
3
|
-
import { t as createDevtoolsHandler } from "./handler-
|
|
1
|
+
import { r as DEVTOOLS_ROUTE_BASE } from "./protocol-ClxYWmhu.js";
|
|
2
|
+
import { h as isLoopbackAddress, v as errorResponse } from "./reflet-proxy-Dg6mea-4.js";
|
|
3
|
+
import { t as createDevtoolsHandler } from "./handler-D8c3woEz.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
|
|
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-D_DrRBeB.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.
|
|
2306
|
+
...activeScreenshot?.context ?? collectPageContext({ sdkVersion: "0.7.8" }),
|
|
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-
|
|
2374
|
-
const TeamLayer = lazy(() => import("./team-layer-
|
|
2372
|
+
const DevtoolsLayer = process.env.NODE_ENV === "development" ? lazy(() => import("./devtools-layer-B_b_3MH3.js").then((module) => ({ default: module.DevtoolsLayer }))) : null;
|
|
2373
|
+
const TeamLayer = lazy(() => import("./team-layer-CxMBo9WN.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. */
|