reflet-sdk 0.7.4 → 0.7.6

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 (34) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +35 -4
  3. package/dist/desktop.d.ts +24 -0
  4. package/dist/desktop.d.ts.map +1 -0
  5. package/dist/desktop.js +118 -0
  6. package/dist/desktop.js.map +1 -0
  7. package/dist/{devtools-layer-CcTmlikp.js → devtools-layer-xN47FKpW.js} +5 -5
  8. package/dist/devtools-layer-xN47FKpW.js.map +1 -0
  9. package/dist/devtools-next.d.ts.map +1 -1
  10. package/dist/devtools-next.js +1 -1
  11. package/dist/{devtools-styles-teRMfSzW.js → devtools-styles-DQpiubGB.js} +26 -8
  12. package/dist/devtools-styles-DQpiubGB.js.map +1 -0
  13. package/dist/devtools-vite.js +2 -1
  14. package/dist/devtools-vite.js.map +1 -1
  15. package/dist/feedback.d.ts +7 -3
  16. package/dist/feedback.d.ts.map +1 -1
  17. package/dist/feedback.js +23 -13
  18. package/dist/feedback.js.map +1 -1
  19. package/dist/{handler-B3_PQB1q.js → handler-ndF3gZBx.js} +11 -416
  20. package/dist/handler-ndF3gZBx.js.map +1 -0
  21. package/dist/reflet-proxy-BW6q9xds.js +467 -0
  22. package/dist/reflet-proxy-BW6q9xds.js.map +1 -0
  23. package/dist/{submit-CG4LXioK.js → submit-DsJwTy3X.js} +25 -9
  24. package/dist/submit-DsJwTy3X.js.map +1 -0
  25. package/dist/{team-layer-zLj_QPQX.js → team-layer-VlQtvR5O.js} +127 -57
  26. package/dist/team-layer-VlQtvR5O.js.map +1 -0
  27. package/dist/team-tools-bridge-C51PpkUI.d.ts +31 -0
  28. package/dist/team-tools-bridge-C51PpkUI.d.ts.map +1 -0
  29. package/package.json +5 -1
  30. package/dist/devtools-layer-CcTmlikp.js.map +0 -1
  31. package/dist/devtools-styles-teRMfSzW.js.map +0 -1
  32. package/dist/handler-B3_PQB1q.js.map +0 -1
  33. package/dist/submit-CG4LXioK.js.map +0 -1
  34. package/dist/team-layer-zLj_QPQX.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ 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.6] - 2026-10-17
9
+
10
+ ### Changed
11
+
12
+ - Pick: the note card's actions are smaller. Pick another and Copy are icon buttons (their names show on hover), and View code is now Code with an icon.
13
+ - Devtools: an empty Board says "This page" or "Whole board" above its message instead of "0 on this page" or "0 on the board".
14
+ - Devtools: the panel has no divider lines under its tabs or above the account footer.
15
+
16
+ ## [0.7.5] - 2026-10-17
17
+
18
+ ### Added
19
+
20
+ - Team tools in desktop apps: `createTeamToolsHost` from `reflet-sdk/desktop` runs them in an Electron main process, and `<RefletFeedback teamTools={bridge} />` takes the bridge it returns. Pages loaded from `file://` can now record sessions: sign-in opens the system browser and returns to a loopback listener, and the token stays in `~/.reflet/devtools/`, out of the page.
21
+
8
22
  ## [0.7.4] - 2026-10-15
9
23
 
10
24
  ### Changed
package/README.md CHANGED
@@ -167,7 +167,7 @@ builds do; production builds strip React's debug data).
167
167
  | `categories` | `("bug" \| "idea" \| "question")[]` | all three | Category chips to show. |
168
168
  | `labels` | `Partial<FeedbackWidgetLabels>` | — | Override any string for i18n. |
169
169
  | `devtools` | `boolean` | `true` | Dev-only team tools; see below. Never loaded outside `NODE_ENV=development`. |
170
- | `teamTools` | `boolean` | `true` | Lets signed-in members of your Reflet organization record sessions in production; see below. |
170
+ | `teamTools` | `boolean \| TeamToolsBridge` | `true` | Lets signed-in members of your Reflet organization record sessions in production; see below. Desktop apps pass a bridge. |
171
171
  | `appUrl` | `string` | `"https://www.reflet.app"` | Reflet web app origin team tools sign in on. |
172
172
  | `onSubmit` | `(result: { feedbackId: string }) => void` | — | Fires after a successful send. |
173
173
 
@@ -192,12 +192,12 @@ builds drop it at bundle time — the devtools chunk is never shipped to your
192
192
  users. Pass `devtools={false}` to turn it off.
193
193
 
194
194
  - **Pick** — click an element to write a note on it. The note card has a
195
- "View code" button, and Shift+click (or Shift+Enter) skips the note and opens
195
+ Code button, and Shift+click (or Shift+Enter) skips the note and opens
196
196
  the source file with the JSX element highlighted, ready to jump to in your
197
197
  editor. Locations come from React's debug stacks resolved through the dev
198
198
  server's source maps, Server Components included. Press ⌘C (Ctrl+C) on the
199
- aimed element, or Copy on the note card, to put the element and its source on
200
- the clipboard for a coding agent, without saving a note.
199
+ aimed element, or the note card's copy button, to put the element and its
200
+ source on the clipboard for a coding agent, without saving a note.
201
201
  - **Notes** — a note appears the moment you press Enter, as a numbered pin on
202
202
  its element, Figma-style; click the pin to read, edit or delete it, and find
203
203
  the same number on its card in the panel. Its source location and a
@@ -299,6 +299,37 @@ transcript is saved, within 24 hours at most. Pausing stops all collection;
299
299
  keystrokes are never recorded, form fields appear empty in screenshots, and
300
300
  password fields and elements marked `data-reflet-redact` are hidden from them.
301
301
 
302
+ ##### Desktop apps (Electron)
303
+
304
+ A page loaded from `file://` has no origin Reflet can bind a sign-in to, so the
305
+ browser flow above can't work there. Run team tools from the app's main
306
+ process instead: sign-in opens the system browser and comes back to a one-shot
307
+ listener on `127.0.0.1`, like the devtools' Connect, and the token stays on disk
308
+ in `~/.reflet/devtools/`, never in the page. Members approve it on reflet.app as
309
+ a local connection; revoke it from Account → Devtools. The key needs no allowed
310
+ domain for this.
311
+
312
+ ```ts
313
+ // main process
314
+ import { app, ipcMain, shell } from 'electron';
315
+ import { createTeamToolsHost } from 'reflet-sdk/desktop';
316
+
317
+ const teamTools = createTeamToolsHost({
318
+ appDataDirectory: app.getPath('userData'),
319
+ openExternal: (url) => shell.openExternal(url),
320
+ publicKey: 'fb_pub_xxx',
321
+ });
322
+ ipcMain.handle('reflet-team-tools:account', () => teamTools.account());
323
+ ipcMain.handle('reflet-team-tools:request', (_event, call) => teamTools.request(call));
324
+ ipcMain.handle('reflet-team-tools:sign-in', () => teamTools.signIn());
325
+ ipcMain.handle('reflet-team-tools:sign-out', () => teamTools.signOut());
326
+ ```
327
+
328
+ Expose the four calls from the preload script, then pass them as the bridge:
329
+ `<RefletFeedback publicKey="fb_pub_xxx" teamTools={bridge} />`. Team tools show
330
+ at once, so only pass the bridge in builds meant for your team. `request` only
331
+ forwards the session and feedback endpoints the devtools use.
332
+
302
333
  ### 5. In-App Surveys
303
334
 
304
335
  Create surveys in the Reflet dashboard (NPS, ratings, choices, text, statements,
@@ -0,0 +1,24 @@
1
+ import { i as TeamToolsReply, n as TeamToolsBridge, r as TeamToolsCall, t as TeamToolsAccount } from "./team-tools-bridge-C51PpkUI.js";
2
+ //#region src/devtools/server/desktop.d.ts
3
+ export interface TeamToolsHostOptions {
4
+ /** Reflet API origin. Defaults to the hosted API. */
5
+ apiUrl?: string;
6
+ /** A directory only this app uses, such as Electron's `app.getPath("userData")`. Keeps its sign-in apart from other apps and dev servers. */
7
+ appDataDirectory: string;
8
+ /** Reflet web app origin sign-in happens on. Defaults to https://www.reflet.app. */
9
+ appUrl?: string;
10
+ /** Opens a URL in the system browser, such as Electron's `shell.openExternal`. */
11
+ openExternal: (url: string) => Promise<void>;
12
+ publicKey: string;
13
+ }
14
+ /**
15
+ * Runs team tools for a desktop app in its privileged process (Electron's
16
+ * main): sign-in goes through the system browser back to a one-shot loopback
17
+ * listener, and the token stays on disk outside the page, like the dev
18
+ * server's Connect. Expose the returned bridge to the page over IPC and pass it
19
+ * to `<RefletFeedback teamTools={bridge} />`.
20
+ */
21
+ export declare function createTeamToolsHost(options: TeamToolsHostOptions): TeamToolsBridge;
22
+ //#endregion
23
+ export type { TeamToolsAccount, TeamToolsBridge, TeamToolsCall, TeamToolsReply };
24
+ //# sourceMappingURL=desktop.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,118 @@
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";
2
+ import { createServer } from "node:http";
3
+ //#region src/devtools/server/desktop.ts
4
+ const LOOPBACK_HOST = "127.0.0.1";
5
+ const SIGN_IN_TIMED_OUT = "Reflet sign-in timed out. Sign in again.";
6
+ async function sendPage(response, page) {
7
+ response.writeHead(page.status, Object.fromEntries(page.headers));
8
+ response.end(await page.text());
9
+ }
10
+ async function listenOnLoopback(server) {
11
+ await new Promise((resolve, reject) => {
12
+ server.once("error", reject);
13
+ server.listen(0, LOOPBACK_HOST, resolve);
14
+ });
15
+ const address = server.address();
16
+ if (address === null || typeof address === "string") throw new Error("The Reflet sign-in listener has no port.");
17
+ return address.port;
18
+ }
19
+ /** Settles on the first callback carrying this sign-in's state; anything else gets a 404 and changes nothing. */
20
+ function awaitConnectCallback(server, pending) {
21
+ const callbackPath = new URL(pending.redirectUri).pathname;
22
+ return new Promise((resolve, reject) => {
23
+ const timeout = setTimeout(() => reject(/* @__PURE__ */ new Error(SIGN_IN_TIMED_OUT)), PENDING_CONNECT_TTL_MS);
24
+ let isRedeeming = false;
25
+ const redeem = async (url, response) => {
26
+ const outcome = await redeemConnectCode(pending.settings, {
27
+ code: url.searchParams.get("code"),
28
+ redirectUri: pending.redirectUri,
29
+ state: pending.state
30
+ });
31
+ await sendPage(response, connectOutcomePage(outcome));
32
+ if (!outcome.ok) throw new Error(outcome.message);
33
+ return { organizationName: outcome.organizationName };
34
+ };
35
+ server.on("request", (request, response) => {
36
+ const url = new URL(request.url ?? "/", pending.redirectUri);
37
+ const isThisSignIn = request.method === "GET" && url.pathname === callbackPath && url.searchParams.get("state") === pending.state;
38
+ if (isRedeeming || !isThisSignIn) {
39
+ sendPage(response, htmlPage("Not found", "Nothing here answers this link.", 404)).catch(() => void 0);
40
+ return;
41
+ }
42
+ isRedeeming = true;
43
+ clearTimeout(timeout);
44
+ redeem(url, response).then(resolve, reject);
45
+ });
46
+ });
47
+ }
48
+ async function startBrowserSignIn(settings, publicKey) {
49
+ const server = createServer();
50
+ const port = await listenOnLoopback(server);
51
+ const redirectUri = connectCallbackUriFor(`http://${LOOPBACK_HOST}:${port}`);
52
+ const { authorizeUrl, state } = connectAuthorizeUrl(settings, {
53
+ publicKey,
54
+ redirectUri
55
+ });
56
+ return {
57
+ account: awaitConnectCallback(server, {
58
+ redirectUri,
59
+ settings,
60
+ state
61
+ }).finally(() => server.close()),
62
+ authorizeUrl
63
+ };
64
+ }
65
+ /**
66
+ * Runs team tools for a desktop app in its privileged process (Electron's
67
+ * main): sign-in goes through the system browser back to a one-shot loopback
68
+ * listener, and the token stays on disk outside the page, like the dev
69
+ * server's Connect. Expose the returned bridge to the page over IPC and pass it
70
+ * to `<RefletFeedback teamTools={bridge} />`.
71
+ */
72
+ function createTeamToolsHost(options) {
73
+ const settings = resolveDevtoolsOptions({
74
+ apiUrl: options.apiUrl,
75
+ appUrl: options.appUrl,
76
+ root: options.appDataDirectory
77
+ }, {});
78
+ let pendingSignIn = null;
79
+ const currentSignIn = () => {
80
+ if (pendingSignIn) return pendingSignIn;
81
+ const started = startBrowserSignIn(settings, options.publicKey);
82
+ pendingSignIn = started;
83
+ const release = () => {
84
+ if (pendingSignIn === started) pendingSignIn = null;
85
+ };
86
+ started.then((signIn) => signIn.account).then(release, release);
87
+ return started;
88
+ };
89
+ return {
90
+ account: async () => {
91
+ const connection = await readConnection(settings.root, settings.apiUrl);
92
+ return connection && { organizationName: connection.organizationName };
93
+ },
94
+ request: async (call) => {
95
+ const search = call.query ? `?${new URLSearchParams(call.query)}` : "";
96
+ const response = await proxyWithBoardCredential(new Request(`${settings.apiUrl}/${search}`, {
97
+ body: call.method === "POST" ? call.body : void 0,
98
+ method: call.method
99
+ }), call.path, settings);
100
+ return {
101
+ body: await response.text(),
102
+ status: response.status
103
+ };
104
+ },
105
+ signIn: async () => {
106
+ const signIn = await currentSignIn();
107
+ await options.openExternal(signIn.authorizeUrl);
108
+ return await signIn.account;
109
+ },
110
+ signOut: async () => {
111
+ await forgetConnection(settings);
112
+ }
113
+ };
114
+ }
115
+ //#endregion
116
+ export { createTeamToolsHost };
117
+
118
+ //# sourceMappingURL=desktop.js.map
@@ -0,0 +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,6 +1,6 @@
1
- import { $ as shortenSourcePath, G as describeElement, M as TargetIcon, Q as resolveComponentStack, R as DEFAULT_WIDGET_LABELS, U as buildElementSelection, V as collectPageContext, X as getFiberFromNode, a as uploadBlob, f as listenOnPageAndWidget, g as isSubmitEnter, it as releaseCapture, nt as capturedFromCanvas, o as ElementPicker, p as listenToKeydown, r as deriveTitle, rt as capturedViewportSize, s as isWidgetOwned, tt as captureViewport, z as SDK_VERSION } from "./submit-CG4LXioK.js";
1
+ import { $ as resolveComponentStack, B as SDK_VERSION, H as collectPageContext, K as describeElement, N as TargetIcon, W as buildElementSelection, Z as getFiberFromNode, a as uploadBlob, at as releaseCapture, et as shortenSourcePath, f as listenOnPageAndWidget, g as isSubmitEnter, it as capturedViewportSize, nt as captureViewport, o as ElementPicker, p as listenToKeydown, r as deriveTitle, rt as capturedFromCanvas, s as isWidgetOwned, z as DEFAULT_WIDGET_LABELS } from "./submit-DsJwTy3X.js";
2
2
  import { t as SelectionOutline } from "./selection-outline-NzAAAZXR.js";
3
- import { A as searchCode, C as devtoolsTransport, D as fetchBoardFeedback, E as disconnectBoard, F as settle, L as Notices, M as committed, N as describeStorageFailure, O as fetchSourceFile, P as openDatabase, R as announce, T as callReflet, _ as RecordingLayer, a as SHEET_TABS, c as AccountNote, d as sessionSheetPlace, f as useSessionNavigation, g as copyToClipboard, h as ViewOptionsIcon, i as DevtoolsBarButtons, j as startBoardConnect, k as probeDevRoute, l as SESSIONS_STYLES, m as RefreshIcon, n as DevtoolsSheet, p as Menu, r as DevtoolsBar, s as AccountFooter, t as DEVTOOLS_STYLES, u as SessionsPanel, v as countLabel, x as useWorkspaceLoader, y as SessionWorkspaceProvider, z as reportFailure } from "./devtools-styles-teRMfSzW.js";
3
+ import { A as probeDevRoute, B as reportFailure, D as disconnectBoard, E as callReflet, F as openDatabase, I as settle, M as startBoardConnect, N as committed, O as fetchBoardFeedback, P as describeStorageFailure, R as Notices, _ as RecordingLayer, a as SHEET_TABS, c as AccountNote, d as sessionSheetPlace, f as useSessionNavigation, g as copyToClipboard, h as ViewOptionsIcon, i as DevtoolsBarButtons, j as searchCode, k as fetchSourceFile, l as SESSIONS_STYLES, m as RefreshIcon, n as DevtoolsSheet, p as Menu, r as DevtoolsBar, s as AccountFooter, t as DEVTOOLS_STYLES, u as SessionsPanel, v as countLabel, w as devtoolsTransport, x as useWorkspaceLoader, y as SessionWorkspaceProvider, z as announce } from "./devtools-styles-DQpiubGB.js";
4
4
  import * as G$1 from "react";
5
5
  import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -448,7 +448,7 @@ function InboxToolbar({ onRefresh, onSelectScope, scope, state }) {
448
448
  children: [
449
449
  /* @__PURE__ */ jsx("span", {
450
450
  className: "dt-status",
451
- children: state.kind === "ready" ? `${state.page.total} ${SCOPE_PLACES[scope]}` : SCOPE_LABELS[scope]
451
+ children: state.kind === "ready" && state.page.total > 0 ? `${state.page.total} ${SCOPE_PLACES[scope]}` : SCOPE_LABELS[scope]
452
452
  }),
453
453
  /* @__PURE__ */ jsx(Menu, {
454
454
  items: INBOX_SCOPES.map((option) => ({
@@ -3719,7 +3719,7 @@ function DevtoolsLayer({ isWidgetOpen, position, publicKey }) {
3719
3719
  return element !== null;
3720
3720
  }, []);
3721
3721
  const inspect = useMemo(() => ({
3722
- label: "View code",
3722
+ label: "Code",
3723
3723
  onInspect: (element) => {
3724
3724
  setIsPicking(false);
3725
3725
  inspectElement(element);
@@ -3815,4 +3815,4 @@ function DevtoolsLayer({ isWidgetOpen, position, publicKey }) {
3815
3815
  //#endregion
3816
3816
  export { DevtoolsLayer };
3817
3817
 
3818
- //# sourceMappingURL=devtools-layer-CcTmlikp.js.map
3818
+ //# sourceMappingURL=devtools-layer-xN47FKpW.js.map