@markbrutx/promptbook-viewer 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/server/api.d.ts +4 -5
- package/dist/server/api.d.ts.map +1 -1
- package/dist/server/api.js +49 -12
- package/dist/server/api.js.map +1 -1
- package/dist/server/book-source.d.ts +24 -0
- package/dist/server/book-source.d.ts.map +1 -1
- package/dist/server/book-source.js +39 -0
- package/dist/server/book-source.js.map +1 -1
- package/dist/server/server.d.ts +1 -1
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +21 -8
- package/dist/server/server.js.map +1 -1
- package/dist/server/workspace.d.ts +13 -0
- package/dist/server/workspace.d.ts.map +1 -0
- package/dist/server/workspace.js +55 -0
- package/dist/server/workspace.js.map +1 -0
- package/dist/shared/types.d.ts +9 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/web/assets/index-BCBuW76o.css +1 -0
- package/dist/web/assets/index-BwIAKPNq.js +51 -0
- package/dist/web/favicon.png +0 -0
- package/dist/web/index.html +3 -2
- package/dist/web/promptbook-logo.png +0 -0
- package/package.json +19 -2
- package/src/index.ts +2 -0
- package/src/server/api.ts +57 -18
- package/src/server/book-source.ts +67 -0
- package/src/server/server.ts +22 -9
- package/src/server/workspace.ts +64 -0
- package/src/shared/types.ts +11 -0
- package/src/web/App.tsx +91 -28
- package/src/web/api.ts +26 -10
- package/src/web/components/Sidebar.tsx +37 -0
- package/src/web/index.html +1 -0
- package/src/web/public/favicon.png +0 -0
- package/src/web/public/promptbook-logo.png +0 -0
- package/src/web/styles.css +35 -0
- package/src/web/types.ts +2 -0
- package/dist/web/assets/index-B2Wxtb-f.css +0 -1
- package/dist/web/assets/index-C8f_6lr_.js +0 -51
package/src/web/api.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
Annotation,
|
|
4
4
|
AnnotationsResponse,
|
|
5
5
|
BookResponse,
|
|
6
|
+
BooksResponse,
|
|
6
7
|
Context,
|
|
7
8
|
LintResponse,
|
|
8
9
|
ResolveResponse,
|
|
@@ -30,15 +31,30 @@ async function sendJson<T>(method: "POST" | "DELETE", path: string, body?: unkno
|
|
|
30
31
|
return (await res.json()) as T;
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
/** Append `?book=<name>` so a request targets the active book in the workspace. */
|
|
35
|
+
function scoped(path: string, book: string | null): string {
|
|
36
|
+
if (book === null || book === "") {
|
|
37
|
+
return path;
|
|
38
|
+
}
|
|
39
|
+
const sep = path.includes("?") ? "&" : "?";
|
|
40
|
+
return `${path}${sep}book=${encodeURIComponent(book)}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
export const api = {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
books: () => getJson<BooksResponse>("/api/books"),
|
|
45
|
+
book: (book: string | null) => getJson<BookResponse>(scoped("/api/book", book)),
|
|
46
|
+
resolve: (book: string | null, prompt: string, context: Context) =>
|
|
47
|
+
sendJson<ResolveResponse>("POST", scoped("/api/resolve", book), { prompt, context }),
|
|
48
|
+
lint: (book: string | null, prompt: string, context: Context) =>
|
|
49
|
+
sendJson<LintResponse>("POST", scoped("/api/lint", book), { prompt, context }),
|
|
50
|
+
usedIn: (book: string | null, fragmentId: string) =>
|
|
51
|
+
getJson<UsedInResponse>(scoped(`/api/used-in/${encodeURIComponent(fragmentId)}`, book)),
|
|
52
|
+
annotations: (book: string | null) => getJson<AnnotationsResponse>(scoped("/api/annotations", book)),
|
|
53
|
+
annotate: (book: string | null, body: AnnotateRequest) =>
|
|
54
|
+
sendJson<Annotation>("POST", scoped("/api/annotate", book), body),
|
|
55
|
+
resolveAnnotation: (book: string | null, id: string) =>
|
|
56
|
+
sendJson<{ id: string; removed: boolean }>(
|
|
57
|
+
"DELETE",
|
|
58
|
+
scoped(`/api/annotations/${encodeURIComponent(id)}`, book),
|
|
59
|
+
),
|
|
44
60
|
};
|
|
@@ -2,8 +2,12 @@ import type { ReactNode } from "react";
|
|
|
2
2
|
import { fragmentAccent } from "../colors.js";
|
|
3
3
|
import type { Selection } from "../selection.js";
|
|
4
4
|
import type { CompositionTreeNode, FragmentGroup, GroupNode } from "../tree.js";
|
|
5
|
+
import type { WorkspaceBook } from "../types.js";
|
|
5
6
|
|
|
6
7
|
interface SidebarProps {
|
|
8
|
+
books: WorkspaceBook[];
|
|
9
|
+
activeBook: string | null;
|
|
10
|
+
onSelectBook: (name: string) => void;
|
|
7
11
|
tree: CompositionTreeNode[];
|
|
8
12
|
fragmentGroups: FragmentGroup[];
|
|
9
13
|
selection: Selection | null;
|
|
@@ -12,6 +16,35 @@ interface SidebarProps {
|
|
|
12
16
|
onSelectFragment: (id: string) => void;
|
|
13
17
|
}
|
|
14
18
|
|
|
19
|
+
/** Brand header + (when the workspace holds more than one book) a book switcher. */
|
|
20
|
+
function BookSwitcher({
|
|
21
|
+
books,
|
|
22
|
+
activeBook,
|
|
23
|
+
onSelectBook,
|
|
24
|
+
}: {
|
|
25
|
+
books: WorkspaceBook[];
|
|
26
|
+
activeBook: string | null;
|
|
27
|
+
onSelectBook: (name: string) => void;
|
|
28
|
+
}) {
|
|
29
|
+
return (
|
|
30
|
+
<header className="sidebar-brand">
|
|
31
|
+
<img className="sidebar-logo" src="/promptbook-logo.png" alt="promptbook" />
|
|
32
|
+
{books.length > 1 ? (
|
|
33
|
+
<label className="book-switcher">
|
|
34
|
+
<span className="book-switcher-label">Book</span>
|
|
35
|
+
<select value={activeBook ?? ""} onChange={(event) => onSelectBook(event.target.value)}>
|
|
36
|
+
{books.map((b) => (
|
|
37
|
+
<option key={b.name} value={b.name}>
|
|
38
|
+
{b.name}
|
|
39
|
+
</option>
|
|
40
|
+
))}
|
|
41
|
+
</select>
|
|
42
|
+
</label>
|
|
43
|
+
) : null}
|
|
44
|
+
</header>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
15
48
|
function isVariantSelected(selection: Selection | null, composition: string, variant: string): boolean {
|
|
16
49
|
return (
|
|
17
50
|
selection?.kind === "variant" && selection.composition === composition && selection.variant === variant
|
|
@@ -129,6 +162,9 @@ function GroupItem({
|
|
|
129
162
|
}
|
|
130
163
|
|
|
131
164
|
export function Sidebar({
|
|
165
|
+
books,
|
|
166
|
+
activeBook,
|
|
167
|
+
onSelectBook,
|
|
132
168
|
tree,
|
|
133
169
|
fragmentGroups,
|
|
134
170
|
selection,
|
|
@@ -139,6 +175,7 @@ export function Sidebar({
|
|
|
139
175
|
const handlers: NodeHandlers = { onSelectVariant, onSelectCode };
|
|
140
176
|
return (
|
|
141
177
|
<nav className="sidebar">
|
|
178
|
+
<BookSwitcher books={books} activeBook={activeBook} onSelectBook={onSelectBook} />
|
|
142
179
|
<section>
|
|
143
180
|
<h2 className="sidebar-title">Compositions</h2>
|
|
144
181
|
{tree.length === 0 ? (
|
package/src/web/index.html
CHANGED
|
Binary file
|
|
Binary file
|
package/src/web/styles.css
CHANGED
|
@@ -34,6 +34,36 @@ body {
|
|
|
34
34
|
background: var(--panel);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
.sidebar-brand {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
gap: 10px;
|
|
41
|
+
padding: 4px 4px 12px;
|
|
42
|
+
border-bottom: 1px solid var(--border);
|
|
43
|
+
margin-bottom: 4px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.sidebar-logo {
|
|
47
|
+
width: 168px;
|
|
48
|
+
max-width: 80%;
|
|
49
|
+
height: auto;
|
|
50
|
+
display: block;
|
|
51
|
+
margin: 8px auto 4px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.book-switcher {
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
gap: 6px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.book-switcher-label {
|
|
61
|
+
font-size: 11px;
|
|
62
|
+
text-transform: uppercase;
|
|
63
|
+
letter-spacing: 0.08em;
|
|
64
|
+
color: var(--muted);
|
|
65
|
+
}
|
|
66
|
+
|
|
37
67
|
.sidebar-title {
|
|
38
68
|
font-size: 11px;
|
|
39
69
|
text-transform: uppercase;
|
|
@@ -201,6 +231,11 @@ input {
|
|
|
201
231
|
background: #fff;
|
|
202
232
|
}
|
|
203
233
|
|
|
234
|
+
.book-switcher select {
|
|
235
|
+
flex: 1;
|
|
236
|
+
width: auto;
|
|
237
|
+
}
|
|
238
|
+
|
|
204
239
|
/* Controls */
|
|
205
240
|
.control-row {
|
|
206
241
|
display: grid;
|
package/src/web/types.ts
CHANGED
|
@@ -6,6 +6,7 @@ export type {
|
|
|
6
6
|
Annotation,
|
|
7
7
|
AnnotationsResponse,
|
|
8
8
|
BookResponse,
|
|
9
|
+
BooksResponse,
|
|
9
10
|
CodePromptSummary,
|
|
10
11
|
CompositionSummary,
|
|
11
12
|
FragmentSummary,
|
|
@@ -14,4 +15,5 @@ export type {
|
|
|
14
15
|
Segment,
|
|
15
16
|
UsedInResponse,
|
|
16
17
|
VariantSummary,
|
|
18
|
+
WorkspaceBook,
|
|
17
19
|
} from "../shared/types.js";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--bg: #fff;--panel: #f6f8fa;--border: #e1e4e8;--text: #1f2328;--muted: #6e7781;--accent: #4f46e5;--warn: #b35900;font-family:system-ui,-apple-system,Segoe UI,sans-serif;font-size:14px;color:var(--text)}*{box-sizing:border-box}body{margin:0;background:var(--bg)}.layout{display:grid;grid-template-columns:260px minmax(0,1fr) 380px;height:100vh}.sidebar{border-right:1px solid var(--border);overflow-y:auto;padding:12px;background:var(--panel)}.sidebar-title{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin:16px 0 6px}.tree{list-style:none;margin:0;padding-left:12px}.tree-folder,.tree-composition{cursor:pointer;font-weight:600;padding:2px 0}.tree-item,.link{display:inline-flex;align-items:center;gap:6px;width:100%;text-align:left;background:none;border:none;padding:3px 6px;border-radius:5px;color:var(--text);cursor:pointer;font:inherit}.tree-item:hover{background:#eaeef2}.tree-item.active{background:var(--accent);color:#fff}.link{width:auto;color:var(--accent);padding:0;text-decoration:underline}.swatch{width:10px;height:10px;border-radius:2px;flex:none}.canvas{overflow-y:auto;padding:20px 24px}.canvas-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:12px}.canvas-title{font-size:18px;margin:0;display:flex;align-items:center;gap:8px}.legend{list-style:none;display:flex;flex-wrap:wrap;gap:10px;padding:0;margin:0 0 14px;font-size:12px;color:var(--muted)}.legend li{display:inline-flex;align-items:center;gap:5px}.prompt{display:flex;flex-direction:column;gap:14px}.segment{position:relative;margin:0;padding:16px 12px 12px;border-radius:6px;border:1px solid var(--border);white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:13px;line-height:1.5}.segment-tag{position:absolute;top:0;right:0;font-size:10px;padding:1px 6px;background:#0000000f;border-bottom-left-radius:6px;font-family:system-ui,sans-serif;color:var(--muted)}.rail{border-left:1px solid var(--border);overflow-y:auto;padding:16px;background:var(--panel)}.rail section,.addons section{margin-bottom:18px}.rail h3,.addons h3{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin:0 0 8px}.rail h4{margin:12px 0 4px;font-size:12px}select,input{width:100%;padding:4px 6px;border:1px solid var(--border);border-radius:5px;font:inherit;background:#fff}.control-row{display:grid;grid-template-columns:90px 1fr;align-items:center;gap:8px;margin-bottom:6px}.control-key{font-family:ui-monospace,monospace;font-size:12px;color:var(--muted)}.control-add{display:grid;grid-template-columns:1fr auto;gap:6px;margin-top:6px}.control-add button{border:1px solid var(--border);border-radius:5px;background:#fff;cursor:pointer;padding:0 10px}.rules,.findings,.warnings,.used-list{list-style:none;margin:0;padding:0}.rules li{padding:4px 0;border-bottom:1px solid var(--border);font-size:12px}.rules li.skipped{color:var(--muted)}.rule-mark{margin-right:6px}.rule-action{font-weight:600;margin-right:6px}.rule-effect{font-family:ui-monospace,monospace;margin-top:2px}.rule-reason{color:var(--muted);margin-top:2px}.order{font-family:ui-monospace,monospace;font-size:12px;word-break:break-word}.finding{display:flex;flex-wrap:wrap;gap:6px;align-items:baseline;padding:5px 0;border-bottom:1px solid var(--border);font-size:12px}.finding-sev{text-transform:uppercase;font-size:10px;font-weight:700;padding:1px 5px;border-radius:4px;background:#eaeef2}.finding.error .finding-sev{background:#ffd7d5;color:#a40e26}.finding.warning .finding-sev{background:#fff1c2;color:var(--warn)}.finding-rule{font-family:ui-monospace,monospace;color:var(--muted)}.diff-body{margin:8px 0 0;font-family:ui-monospace,monospace;font-size:12px;border:1px solid var(--border);border-radius:6px;overflow-x:auto;background:#fff}.diff-row{padding:0 8px;white-space:pre-wrap}.diff-row.add{background:#e6ffec}.diff-row.remove{background:#ffebe9}.diff-mark{display:inline-block;width:12px;color:var(--muted)}.annot-mark{background:#fff3b0;border-bottom:2px solid var(--warn);border-radius:2px;padding:0 1px}.annot-popover{position:fixed;z-index:20;width:280px;padding:10px;background:#fff;border:1px solid var(--border);border-radius:8px;box-shadow:0 8px 24px #00000029}.annot-popover textarea{width:100%;min-height:64px;resize:vertical;margin:6px 0}.annot-actions{display:flex;justify-content:flex-end;gap:10px;align-items:center}.annot-actions button[type=button]:last-child{border:1px solid var(--border);border-radius:5px;background:var(--accent);color:#fff;padding:4px 12px;cursor:pointer}.annot-actions button[disabled]{opacity:.5;cursor:default}.annot-quote{margin:0;font-style:italic;color:var(--muted);font-size:12px;word-break:break-word}.annot-list{list-style:none;margin:0;padding:0}.annot-list li{padding:8px 0;border-bottom:1px solid var(--border)}.annot-comment{margin:4px 0;font-size:13px;word-break:break-word}.annot-meta{display:flex;justify-content:space-between;align-items:center;gap:8px;font-size:12px}.badge{font-size:12px;padding:2px 8px;border-radius:999px;background:#eaeef2;white-space:nowrap}.badge-code{font-size:10px;text-transform:uppercase;letter-spacing:.06em;background:#2b2b40;color:#fff}.code-tabs{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:12px}.code-tab{font:inherit;font-size:12px;padding:3px 10px;border-radius:999px;border:1px solid var(--border);background:var(--panel);color:var(--text);cursor:pointer}.code-tab.active{background:var(--accent);border-color:var(--accent)}.code-diff{margin-top:18px}.tokens{font-size:20px;font-weight:600;margin:0}.muted{color:var(--muted)}.warn{color:var(--warn)}.used-list li{padding:3px 0}
|