@backmatter/studio 0.0.15 → 0.0.17

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 (80) hide show
  1. package/dist/chunks/{UiKitApp-BDsrXN12.js → UiKitApp-D7vllRQE.js} +2 -2
  2. package/dist/chunks/{WorkspaceRoutes-BTZ1Ah4F.js → WorkspaceRoutes-DH1LD-2Q.js} +30013 -27965
  3. package/dist/chunks/{dashboard-FT7dm5wi.js → dashboard-Dsaeud2x.js} +55 -54
  4. package/dist/chunks/{define-studio-BYtYDGLX.js → define-studio-DlYjgrwr.js} +12 -16
  5. package/dist/chunks/{index-DHJvh5Uy.js → index-CGMqw2NH.js} +1 -1
  6. package/dist/dashboard.js +2 -2
  7. package/dist/define.js +1 -1
  8. package/dist/index.js +4 -4
  9. package/dist/site-widget.css +49 -1
  10. package/dist/site-widget.js +8341 -8058
  11. package/dist/studio.css +279 -13
  12. package/package.json +2 -2
  13. package/src/WorkspaceRoutes.tsx +5 -2
  14. package/src/api.ts +82 -7
  15. package/src/chat/ChangeCard.tsx +8 -2
  16. package/src/chat/ChatMessage.tsx +171 -102
  17. package/src/chat/ChatPanel.tsx +54 -34
  18. package/src/chat/ChatProvider.tsx +122 -20
  19. package/src/chat/Composer.tsx +51 -8
  20. package/src/chat/PointerChip.tsx +9 -5
  21. package/src/chat/ThreadHistory.tsx +3 -3
  22. package/src/chat/chat-context.ts +67 -3
  23. package/src/chat/chat-helpers.ts +12 -0
  24. package/src/chat/chat-state.ts +11 -0
  25. package/src/chrome/AppShell.tsx +16 -5
  26. package/src/chrome/CommandPalette.tsx +12 -0
  27. package/src/chrome/ContentExplorerSidebar.tsx +23 -3
  28. package/src/chrome/Toast.tsx +115 -0
  29. package/src/chrome/UserMenu.tsx +34 -3
  30. package/src/chrome/WorkspaceStatusBar.tsx +221 -30
  31. package/src/chrome/commit-error.ts +13 -0
  32. package/src/chrome/content-api-url.ts +0 -78
  33. package/src/chrome/content-tree-visibility.ts +13 -3
  34. package/src/chrome/explorer-icons.tsx +5 -0
  35. package/src/chrome/pull-failure.ts +61 -0
  36. package/src/code-session-entry.ts +9 -0
  37. package/src/components/WorkspaceSwitcher.tsx +2 -2
  38. package/src/element-origin.ts +149 -0
  39. package/src/fields/FieldControl.tsx +748 -0
  40. package/src/fields/choose-field-input.ts +64 -0
  41. package/src/fields/load-relation-documents.ts +35 -0
  42. package/src/fields/omit-field.ts +12 -0
  43. package/src/fields/relation-targets.ts +44 -0
  44. package/src/fields/resolve-object-fields.ts +21 -0
  45. package/src/host-pointer-bootstrap.ts +11 -0
  46. package/src/host-pointer.ts +147 -0
  47. package/src/install-preview-pointer.ts +19 -3
  48. package/src/pages/BlockSettingsDialog.tsx +19 -115
  49. package/src/pages/BlocksList.tsx +43 -35
  50. package/src/pages/BodyProse.tsx +16 -1
  51. package/src/pages/CodeSessionFiles.tsx +221 -0
  52. package/src/pages/CodeSessionPage.tsx +1361 -0
  53. package/src/pages/DocEditorPage.tsx +120 -107
  54. package/src/pages/PreviewSize.tsx +181 -0
  55. package/src/pages/RenderedBody.tsx +17 -2
  56. package/src/pages/SettingsGeneralPage.tsx +67 -2
  57. package/src/pages/WorkspaceHomePage.tsx +313 -0
  58. package/src/pages/WorkspaceLayout.tsx +10 -1
  59. package/src/pages/branch-notice.ts +16 -0
  60. package/src/pages/code-session-boot.ts +43 -0
  61. package/src/pages/code-session-context.tsx +53 -0
  62. package/src/pages/code-session-files.ts +42 -0
  63. package/src/pages/code-session-frame.ts +52 -0
  64. package/src/pages/code-session-routes.ts +42 -0
  65. package/src/pages/home-rows.ts +140 -0
  66. package/src/pages/preview-size.ts +33 -0
  67. package/src/pages/session-preview.ts +109 -0
  68. package/src/pages/use-session-preview.ts +70 -0
  69. package/src/replica/store.ts +11 -0
  70. package/src/replica/types.ts +6 -0
  71. package/src/shell-resets.css +15 -0
  72. package/src/site-pointer.ts +24 -5
  73. package/src/studio-config.ts +1 -1
  74. package/src/ui/Input.tsx +178 -20
  75. package/src/ui/ListRow.tsx +12 -3
  76. package/src/ui/Page.tsx +12 -1
  77. package/src/ui/popup.ts +11 -2
  78. package/src/workspace-path.ts +35 -3
  79. package/src/chrome/ContentApiPopover.tsx +0 -215
  80. package/src/pages/WorkspaceHomeRedirect.tsx +0 -17
@@ -0,0 +1,149 @@
1
+ // packages/studio/src/element-origin.ts
2
+ // Where a rendered element comes from, read from the React fiber the dev server
3
+ // already attached. A production build or a non-React host yields nothing.
4
+
5
+ export type ElementOrigin = {
6
+ component: string | null;
7
+ file: string | null;
8
+ line: number | null;
9
+ column: number | null;
10
+ stack: string[];
11
+ };
12
+
13
+ const STACK_CAP = 4;
14
+ const SOURCE_ROOT = /(?:^|\/)((?:src|app|pages|components|blocks|lib|content)\/[\w./@+-]+\.(?:tsx|ts|jsx|js|mdx))$/;
15
+ const SOURCE_FILE = /^[\w./@+-]+\.(?:tsx|ts|jsx|js|mdx)$/;
16
+
17
+ type Fiber = {
18
+ type?: unknown;
19
+ elementType?: unknown;
20
+ return?: Fiber | null;
21
+ _debugStack?: unknown;
22
+ _debugInfo?: unknown;
23
+ };
24
+
25
+ function fiberOf(element: Element): Fiber | null {
26
+ for (const key of Object.keys(element)) {
27
+ if (!key.startsWith("__reactFiber$") && !key.startsWith("__reactInternalInstance$")) continue;
28
+ const value = (element as unknown as Record<string, unknown>)[key];
29
+ if (value && typeof value === "object") return value as Fiber;
30
+ }
31
+ return null;
32
+ }
33
+
34
+ function functionName(value: unknown): string | null {
35
+ if (typeof value === "function") {
36
+ const named = value as { displayName?: string; name?: string };
37
+ const name = named.displayName || named.name;
38
+ return name && name !== "Unknown" ? name : null;
39
+ }
40
+ if (!value || typeof value !== "object") return null;
41
+ const record = value as { displayName?: string; render?: unknown; type?: unknown };
42
+ if (typeof record.displayName === "string" && record.displayName) return record.displayName;
43
+ const rendered = functionName(record.render);
44
+ if (rendered) return rendered;
45
+ return functionName(record.type);
46
+ }
47
+
48
+ function componentName(fiber: Fiber): string | null {
49
+ const named = functionName(fiber.type);
50
+ if (named) return named;
51
+ return functionName(fiber.elementType);
52
+ }
53
+
54
+ function pushName(names: string[], name: string | null) {
55
+ if (!name || names.includes(name) || names.length >= STACK_CAP) return;
56
+ names.push(name);
57
+ }
58
+
59
+ function normalizeFile(raw: string): string | null {
60
+ let file = raw.trim();
61
+ file = file.replace(/^webpack-internal:\/\/\/(?:\([^)]*\)\/)?/, "");
62
+ file = file.replace(/^webpack:\/\/\/?/, "");
63
+ if (file.startsWith("./")) file = file.slice(2);
64
+ if (file.startsWith("file://")) {
65
+ try {
66
+ file = decodeURIComponent(new URL(file).pathname);
67
+ } catch {
68
+ return null;
69
+ }
70
+ }
71
+ file = file.split(/[?#]/)[0] ?? "";
72
+ if (/node_modules|react-dom|react-jsx|next\/dist|webpack\/bootstrap|_next\/|https?:/i.test(file)) return null;
73
+ const rooted = file.match(SOURCE_ROOT);
74
+ if (rooted?.[1]) return rooted[1];
75
+ if (SOURCE_FILE.test(file) && !file.startsWith("/") && !file.includes("..")) return file;
76
+ return null;
77
+ }
78
+
79
+ function frameAt(line: string): { file: string; line: number; column: number } | null {
80
+ const trimmed = line.trim();
81
+ if (!trimmed || trimmed.includes("react-stack-top-frame") || trimmed.includes("react_stack_bottom_frame")) {
82
+ return null;
83
+ }
84
+ const chrome = trimmed.match(/^at\s+(?:async\s+)?(?:.*?\s+\()?(.+?):(\d+):(\d+)\)?$/);
85
+ const firefox = chrome ? null : trimmed.match(/^(?:.*@)?(.+?):(\d+):(\d+)$/);
86
+ const match = chrome ?? firefox;
87
+ if (!match?.[1]) return null;
88
+ const file = normalizeFile(match[1]);
89
+ if (!file) return null;
90
+ const lineNo = Number(match[2]);
91
+ const column = Number(match[3]);
92
+ if (!Number.isInteger(lineNo) || lineNo < 1 || !Number.isInteger(column) || column < 1) return null;
93
+ return { file, line: lineNo, column };
94
+ }
95
+
96
+ function stackText(value: unknown): string {
97
+ if (typeof value === "string") return value;
98
+ if (value && typeof value === "object" && "stack" in value) {
99
+ const stack = (value as { stack?: unknown }).stack;
100
+ return typeof stack === "string" ? stack : "";
101
+ }
102
+ return "";
103
+ }
104
+
105
+ function locationFromStack(value: unknown): { file: string; line: number; column: number } | null {
106
+ const text = stackText(value);
107
+ if (!text) return null;
108
+ for (const line of text.split("\n")) {
109
+ if (line.includes("react_stack_bottom_frame")) break;
110
+ const frame = frameAt(line);
111
+ if (frame) return frame;
112
+ }
113
+ return null;
114
+ }
115
+
116
+ function debugNames(value: unknown, names: string[]) {
117
+ if (!Array.isArray(value)) return;
118
+ for (const entry of value) {
119
+ if (!entry || typeof entry !== "object") continue;
120
+ const name = (entry as { name?: unknown }).name;
121
+ if (typeof name === "string") pushName(names, name);
122
+ }
123
+ }
124
+
125
+ /** The component and source behind an element, when the dev fiber exposes them. */
126
+ export function readElementOrigin(element: Element): ElementOrigin | null {
127
+ const names: string[] = [];
128
+ let file: string | null = null;
129
+ let line: number | null = null;
130
+ let column: number | null = null;
131
+ let fiber = fiberOf(element);
132
+ const seen = new Set<Fiber>();
133
+ while (fiber && !seen.has(fiber) && names.length < STACK_CAP) {
134
+ seen.add(fiber);
135
+ pushName(names, componentName(fiber));
136
+ debugNames(fiber._debugInfo, names);
137
+ if (!file) {
138
+ const located = locationFromStack(fiber._debugStack);
139
+ if (located) {
140
+ file = located.file;
141
+ line = located.line;
142
+ column = located.column;
143
+ }
144
+ }
145
+ fiber = fiber.return ?? null;
146
+ }
147
+ if (names.length === 0 && !file) return null;
148
+ return { component: names[0] ?? null, file, line, column, stack: names };
149
+ }