wiki-viewer 1.0.0 → 1.0.1

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 (128) hide show
  1. package/.next/standalone/README.md +328 -99
  2. package/.next/standalone/agents/bootstrap-prompt.md +1 -0
  3. package/.next/standalone/agents/wiki-viewer-skill/SKILL.md +236 -0
  4. package/.next/standalone/bin/wiki-viewer.js +57 -4
  5. package/.next/standalone/docs/agent-collab-plan.md +1615 -0
  6. package/.next/standalone/docs/agent-collab-v2-plan.md +771 -0
  7. package/.next/standalone/package.json +19 -2
  8. package/.next/standalone/pnpm-lock.yaml +1368 -325
  9. package/.next/standalone/pnpm-workspace.yaml +7 -1
  10. package/.next/standalone/src/app/api/agent/activity/route.ts +58 -0
  11. package/.next/standalone/src/app/api/agent/admin/agents/[agentId]/revoke/route.ts +40 -0
  12. package/.next/standalone/src/app/api/agent/admin/agents/route.ts +33 -0
  13. package/.next/standalone/src/app/api/agent/admin/registrations/[regId]/approve/route.ts +83 -0
  14. package/.next/standalone/src/app/api/agent/admin/registrations/[regId]/deny/route.ts +36 -0
  15. package/.next/standalone/src/app/api/agent/admin/registrations/route.ts +27 -0
  16. package/.next/standalone/src/app/api/agent/events/[...path]/route.ts +136 -0
  17. package/.next/standalone/src/app/api/agent/files/[...path]/route.ts +202 -0
  18. package/.next/standalone/src/app/api/agent/internal/span/route.ts +117 -0
  19. package/.next/standalone/src/app/api/agent/register/[regId]/route.ts +50 -0
  20. package/.next/standalone/src/app/api/agent/register/route.ts +110 -0
  21. package/.next/standalone/src/app/api/agent/settings/route.ts +30 -0
  22. package/.next/standalone/src/app/api/agent/settings/token/regenerate/route.ts +20 -0
  23. package/.next/standalone/src/app/api/agent/sidecar/[...path]/route.ts +49 -0
  24. package/.next/standalone/src/app/api/agents/install/route.ts +83 -0
  25. package/.next/standalone/src/app/api/agents/skill/route.ts +20 -0
  26. package/.next/standalone/src/app/api/agents/skill.tar.gz/route.ts +87 -0
  27. package/.next/standalone/src/app/api/auth/[...all]/route.ts +7 -0
  28. package/.next/standalone/src/app/api/owner/init/route.ts +14 -0
  29. package/.next/standalone/src/app/api/system/auth-settings/route.ts +85 -0
  30. package/.next/standalone/src/app/api/system/browse/route.ts +4 -0
  31. package/.next/standalone/src/app/api/system/clear-root/route.ts +8 -1
  32. package/.next/standalone/src/app/api/system/config/route.ts +5 -1
  33. package/.next/standalone/src/app/api/system/pins/route.ts +7 -0
  34. package/.next/standalone/src/app/api/system/reveal/route.ts +7 -0
  35. package/.next/standalone/src/app/api/system/root-status/route.ts +5 -1
  36. package/.next/standalone/src/app/api/system/set-root/route.ts +7 -0
  37. package/.next/standalone/src/app/api/wiki/app/route.ts +15 -0
  38. package/.next/standalone/src/app/api/wiki/content/route.ts +110 -11
  39. package/.next/standalone/src/app/api/wiki/folder/route.ts +7 -0
  40. package/.next/standalone/src/app/api/wiki/move/route.ts +7 -0
  41. package/.next/standalone/src/app/api/wiki/new-file/route.ts +55 -0
  42. package/.next/standalone/src/app/api/wiki/page/route.ts +7 -0
  43. package/.next/standalone/src/app/api/wiki/route.ts +10 -0
  44. package/.next/standalone/src/app/api/wiki/slugs/route.ts +5 -1
  45. package/.next/standalone/src/app/api/wiki/upload/route.ts +7 -0
  46. package/.next/standalone/src/app/api/wiki/watch/route.ts +6 -1
  47. package/.next/standalone/src/app/globals.css +70 -0
  48. package/.next/standalone/src/app/page.tsx +461 -217
  49. package/.next/standalone/src/app/signin/page.tsx +217 -0
  50. package/.next/standalone/src/components/ai-panel/activity-row.tsx +64 -0
  51. package/.next/standalone/src/components/ai-panel/ai-panel.tsx +322 -0
  52. package/.next/standalone/src/components/ai-panel/token-section.tsx +312 -0
  53. package/.next/standalone/src/components/auth-settings-sheet.tsx +209 -0
  54. package/.next/standalone/src/components/editor/bubble-menu.tsx +41 -2
  55. package/.next/standalone/src/components/editor/comment-pip.tsx +56 -0
  56. package/.next/standalone/src/components/editor/comment-thread.tsx +252 -0
  57. package/.next/standalone/src/components/editor/editor.tsx +513 -10
  58. package/.next/standalone/src/components/editor/extensions/proof-span.ts +60 -0
  59. package/.next/standalone/src/components/editor/extensions.ts +2 -0
  60. package/.next/standalone/src/components/editor/proof-span-popover.tsx +161 -0
  61. package/.next/standalone/src/components/editor/suggest-edit-popover.tsx +228 -0
  62. package/.next/standalone/src/components/editor/suggestion-card.tsx +201 -0
  63. package/.next/standalone/src/components/view-width-toggle.tsx +47 -0
  64. package/.next/standalone/src/components/wiki/markdown-preview.tsx +120 -0
  65. package/.next/standalone/src/lib/auth/allowlist.ts +50 -0
  66. package/.next/standalone/src/lib/auth/client.ts +4 -0
  67. package/.next/standalone/src/lib/auth/csrf.ts +68 -0
  68. package/.next/standalone/src/lib/auth/server.ts +164 -0
  69. package/.next/standalone/src/lib/config.ts +4 -0
  70. package/.next/standalone/src/lib/markdown/parse-frontmatter.ts +20 -0
  71. package/.next/standalone/src/lib/markdown/sanitize-schema.ts +106 -0
  72. package/.next/standalone/src/lib/markdown/to-html.ts +46 -8
  73. package/.next/standalone/src/lib/markdown/to-markdown.ts +14 -0
  74. package/.next/standalone/src/lib/proof/activity-shared.ts +31 -0
  75. package/.next/standalone/src/lib/proof/activity.ts +74 -0
  76. package/.next/standalone/src/lib/proof/auth.ts +132 -0
  77. package/.next/standalone/src/lib/proof/block-refs.ts +133 -0
  78. package/.next/standalone/src/lib/proof/blocks.ts +73 -0
  79. package/.next/standalone/src/lib/proof/client-auth.ts +23 -0
  80. package/.next/standalone/src/lib/proof/event-bus.ts +47 -0
  81. package/.next/standalone/src/lib/proof/file-lock.ts +38 -0
  82. package/.next/standalone/src/lib/proof/glob.ts +51 -0
  83. package/.next/standalone/src/lib/proof/idempotency.ts +32 -0
  84. package/.next/standalone/src/lib/proof/mutex.ts +32 -0
  85. package/.next/standalone/src/lib/proof/ops-applier.ts +678 -0
  86. package/.next/standalone/src/lib/proof/owner-auth.ts +2 -0
  87. package/.next/standalone/src/lib/proof/pending.ts +97 -0
  88. package/.next/standalone/src/lib/proof/proof-span.ts +141 -0
  89. package/.next/standalone/src/lib/proof/rate-limit.ts +53 -0
  90. package/.next/standalone/src/lib/proof/register-rate-limit.ts +53 -0
  91. package/.next/standalone/src/lib/proof/registry.ts +190 -0
  92. package/.next/standalone/src/lib/proof/sidecar.ts +66 -0
  93. package/.next/standalone/src/lib/proof/suggest-capture.ts +69 -0
  94. package/.next/standalone/src/lib/proof/types.ts +170 -0
  95. package/.next/standalone/src/lib/proof-config.ts +14 -0
  96. package/.next/standalone/src/middleware.ts +38 -0
  97. package/.next/standalone/src/stores/ai-panel-store.ts +58 -3
  98. package/.next/standalone/src/stores/editor-store.ts +115 -9
  99. package/.next/standalone/src/stores/proof-store.ts +123 -0
  100. package/.next/standalone/src/stores/view-width-store.ts +62 -0
  101. package/.next/standalone/src/tests/proof/activity-aggregator.test.ts +146 -0
  102. package/.next/standalone/src/tests/proof/agent-ownership.test.ts +140 -0
  103. package/.next/standalone/src/tests/proof/agents-install.test.ts +57 -0
  104. package/.next/standalone/src/tests/proof/better-auth.test.ts +68 -0
  105. package/.next/standalone/src/tests/proof/block-refs.test.ts +108 -0
  106. package/.next/standalone/src/tests/proof/blocks.test.ts +92 -0
  107. package/.next/standalone/src/tests/proof/comments-ops.test.ts +177 -0
  108. package/.next/standalone/src/tests/proof/editor-roundtrip.test.ts +85 -0
  109. package/.next/standalone/src/tests/proof/external-edit.test.ts +58 -0
  110. package/.next/standalone/src/tests/proof/file-lock.test.ts +80 -0
  111. package/.next/standalone/src/tests/proof/glob.test.ts +82 -0
  112. package/.next/standalone/src/tests/proof/helpers/auth-session.ts +27 -0
  113. package/.next/standalone/src/tests/proof/markdown-to-html.test.ts +46 -0
  114. package/.next/standalone/src/tests/proof/ops-applier.test.ts +368 -0
  115. package/.next/standalone/src/tests/proof/owner-init.test.ts +2 -0
  116. package/.next/standalone/src/tests/proof/parse-frontmatter.test.ts +60 -0
  117. package/.next/standalone/src/tests/proof/proof-span.test.ts +98 -0
  118. package/.next/standalone/src/tests/proof/rate-limit.test.ts +54 -0
  119. package/.next/standalone/src/tests/proof/registration-flow.test.ts +330 -0
  120. package/.next/standalone/src/tests/proof/registry.test.ts +169 -0
  121. package/.next/standalone/src/tests/proof/routes.test.ts +516 -0
  122. package/.next/standalone/src/tests/proof/sidecar-trim.test.ts +58 -0
  123. package/.next/standalone/src/tests/proof/suggestion-ops.test.ts +280 -0
  124. package/.next/standalone/src/tests/proof/wiki-content-put.test.ts +140 -0
  125. package/.next/standalone/src/tests/proof/wiki-routes-auth.test.ts +208 -0
  126. package/README.md +328 -99
  127. package/bin/wiki-viewer.js +57 -4
  128. package/package.json +19 -2
@@ -0,0 +1,252 @@
1
+ "use client";
2
+
3
+ import * as Popover from "@radix-ui/react-popover";
4
+ import { useEffect, useRef, useState } from "react";
5
+ import { authHeaders } from "@/lib/proof/client-auth";
6
+ import { useProofStore } from "@/stores/proof-store";
7
+ import type { Comment } from "@/lib/proof/types";
8
+
9
+ // ── helpers ──────────────────────────────────────────────────────────────────
10
+
11
+ const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
12
+
13
+ function relTime(iso: string): string {
14
+ const diff = Date.now() - new Date(iso).getTime();
15
+ const secs = Math.round(diff / 1000);
16
+ if (Math.abs(secs) < 60) return rtf.format(-secs, "second");
17
+ const mins = Math.round(secs / 60);
18
+ if (Math.abs(mins) < 60) return rtf.format(-mins, "minute");
19
+ const hrs = Math.round(mins / 60);
20
+ if (Math.abs(hrs) < 24) return rtf.format(-hrs, "hour");
21
+ return rtf.format(-Math.round(hrs / 24), "day");
22
+ }
23
+
24
+ async function postOp(
25
+ path: string,
26
+ baseRevision: number,
27
+ by: string,
28
+ ops: object[],
29
+ ): Promise<{ ok: boolean; stale: boolean; newRevision?: number }> {
30
+ const encoded = encodeURIComponent(path).replace(/%2F/g, "/");
31
+ const res = await fetch(`/api/agent/files/${encoded}`, {
32
+ method: "POST",
33
+ headers: {
34
+ "Content-Type": "application/json",
35
+ "Idempotency-Key": crypto.randomUUID(),
36
+ ...authHeaders(),
37
+ },
38
+ body: JSON.stringify({ baseRevision, by, ops }),
39
+ });
40
+ if (res.status === 409) {
41
+ const data = (await res.json()) as { code?: string; snapshot?: { revision?: number } };
42
+ if (data.code === "STALE_REVISION" && data.snapshot?.revision !== undefined) {
43
+ return { ok: false, stale: true, newRevision: data.snapshot.revision };
44
+ }
45
+ return { ok: false, stale: false };
46
+ }
47
+ if (!res.ok) return { ok: false, stale: false };
48
+ return { ok: true, stale: false };
49
+ }
50
+
51
+ // ── component ────────────────────────────────────────────────────────────────
52
+
53
+ interface Props {
54
+ path: string;
55
+ blockRef: string;
56
+ /** Existing comments on this block (may be empty = new-comment mode). */
57
+ comments: Comment[];
58
+ anchorEl: HTMLElement | null;
59
+ onClose: () => void;
60
+ }
61
+
62
+ export function CommentThread({ path, blockRef, comments, anchorEl, onClose }: Props) {
63
+ const [anchor, setAnchor] = useState<{ top: number; left: number } | null>(null);
64
+ const [text, setText] = useState("");
65
+ const [busy, setBusy] = useState(false);
66
+ const textareaRef = useRef<HTMLTextAreaElement>(null);
67
+
68
+ const openComments = comments.filter((c) => !c.resolved);
69
+ const hasOpen = openComments.length > 0;
70
+ // Use first open comment for reply/resolve; fall back to any comment
71
+ const activeComment = openComments[0] ?? comments[0] ?? null;
72
+
73
+ useEffect(() => {
74
+ if (!anchorEl) {
75
+ setAnchor(null);
76
+ return;
77
+ }
78
+ const rect = anchorEl.getBoundingClientRect();
79
+ setAnchor({ top: rect.bottom + 4, left: rect.left });
80
+ }, [anchorEl]);
81
+
82
+ useEffect(() => {
83
+ if (!anchorEl) return;
84
+ const onKey = (e: KeyboardEvent) => {
85
+ if (e.key === "Escape") onClose();
86
+ };
87
+ window.addEventListener("keydown", onKey);
88
+ return () => window.removeEventListener("keydown", onKey);
89
+ }, [anchorEl, onClose]);
90
+
91
+ useEffect(() => {
92
+ if (anchor) {
93
+ setTimeout(() => textareaRef.current?.focus(), 50);
94
+ }
95
+ }, [anchor]);
96
+
97
+ if (!anchorEl || !anchor) return null;
98
+
99
+ function getRevision(): number {
100
+ const entry = useProofStore.getState().byPath[path];
101
+ return entry?.snapshotRevision ?? 0;
102
+ }
103
+
104
+ async function handleSend() {
105
+ if (!text.trim() || busy) return;
106
+ setBusy(true);
107
+ try {
108
+ const sendOp = activeComment
109
+ ? { type: "comment.reply", commentId: activeComment.id, text: text.trim() }
110
+ : { type: "comment.add", ref: blockRef, text: text.trim() };
111
+
112
+ let rev = getRevision();
113
+ let result = await postOp(path, rev, "human", [sendOp]);
114
+ if (!result.ok && result.stale && result.newRevision !== undefined) {
115
+ // Retry once with fresh revision
116
+ await useProofStore.getState().loadSidecar(path);
117
+ rev = result.newRevision;
118
+ result = await postOp(path, rev, "human", [sendOp]);
119
+ }
120
+ if (result.ok) {
121
+ await useProofStore.getState().loadSidecar(path);
122
+ setText("");
123
+ onClose();
124
+ }
125
+ } finally {
126
+ setBusy(false);
127
+ }
128
+ }
129
+
130
+ async function handleResolveToggle() {
131
+ if (!activeComment || busy) return;
132
+ setBusy(true);
133
+ try {
134
+ const opType = activeComment.resolved ? "comment.reopen" : "comment.resolve";
135
+ let rev = getRevision();
136
+ let result = await postOp(path, rev, "human", [
137
+ { type: opType, commentId: activeComment.id },
138
+ ]);
139
+ if (!result.ok && result.stale && result.newRevision !== undefined) {
140
+ await useProofStore.getState().loadSidecar(path);
141
+ rev = result.newRevision;
142
+ result = await postOp(path, rev, "human", [
143
+ { type: opType, commentId: activeComment.id },
144
+ ]);
145
+ }
146
+ if (result.ok) {
147
+ await useProofStore.getState().loadSidecar(path);
148
+ onClose();
149
+ }
150
+ } finally {
151
+ setBusy(false);
152
+ }
153
+ }
154
+
155
+ // Collect all turns from all comments on this block for display
156
+ const allTurns = comments.flatMap((c) =>
157
+ c.turns.map((t) => ({ ...t, resolved: c.resolved, commentId: c.id })),
158
+ );
159
+
160
+ return (
161
+ <Popover.Root open>
162
+ <Popover.Anchor asChild>
163
+ <span
164
+ aria-hidden="true"
165
+ style={{
166
+ position: "fixed",
167
+ top: anchor.top,
168
+ left: anchor.left,
169
+ width: 0,
170
+ height: 0,
171
+ pointerEvents: "none",
172
+ }}
173
+ />
174
+ </Popover.Anchor>
175
+ <Popover.Portal>
176
+ <Popover.Content
177
+ side="bottom"
178
+ align="start"
179
+ sideOffset={4}
180
+ collisionPadding={8}
181
+ onInteractOutside={onClose}
182
+ className="z-50 w-[min(18rem,calc(100vw-1rem))] bg-popover border border-border rounded-lg shadow-xl p-3 space-y-2 text-[12px] focus:outline-none"
183
+ >
184
+ {/* Header */}
185
+ <div className="flex items-center justify-between">
186
+ <span className="text-[11px] font-mono text-muted-foreground/60 truncate">
187
+ {blockRef}
188
+ </span>
189
+ {activeComment && (
190
+ <button
191
+ type="button"
192
+ disabled={busy}
193
+ onClick={() => void handleResolveToggle()}
194
+ className="ml-2 shrink-0 text-[10px] px-1.5 py-0.5 rounded border border-border hover:bg-accent disabled:opacity-50 transition-colors"
195
+ >
196
+ {activeComment.resolved ? "Reopen" : "Resolve"}
197
+ </button>
198
+ )}
199
+ </div>
200
+
201
+ {/* Turns */}
202
+ {allTurns.length > 0 && (
203
+ <div className="space-y-2 max-h-48 overflow-y-auto pr-1">
204
+ {allTurns.map((t, i) => (
205
+ <div key={i} className="space-y-0.5">
206
+ <p className="text-[10px] text-muted-foreground/60">
207
+ {t.by} · {relTime(t.at)}
208
+ </p>
209
+ <p className="text-foreground leading-snug whitespace-pre-wrap">{t.text}</p>
210
+ </div>
211
+ ))}
212
+ </div>
213
+ )}
214
+
215
+ {/* Reply / new comment footer */}
216
+ {(!activeComment?.resolved || comments.length === 0) && (
217
+ <div className="space-y-1.5 pt-1">
218
+ <textarea
219
+ ref={textareaRef}
220
+ value={text}
221
+ onChange={(e) => setText(e.target.value)}
222
+ onKeyDown={(e) => {
223
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
224
+ void handleSend();
225
+ }
226
+ }}
227
+ placeholder={
228
+ hasOpen ? "Reply…" : "Add a comment…"
229
+ }
230
+ rows={2}
231
+ className="w-full resize-none rounded-md border border-border bg-background px-2 py-1.5 text-[12px] focus:outline-none focus-visible:ring-1 focus-visible:ring-ring placeholder:text-muted-foreground/40"
232
+ />
233
+ <div className="flex items-center justify-between">
234
+ <span className="text-[10px] text-muted-foreground/40">⌘↵ send</span>
235
+ <button
236
+ type="button"
237
+ disabled={busy || !text.trim()}
238
+ onClick={() => void handleSend()}
239
+ className="px-2.5 py-1 rounded-md bg-primary text-primary-foreground text-[11px] font-medium hover:bg-primary/90 disabled:opacity-50 transition-colors"
240
+ >
241
+ Send
242
+ </button>
243
+ </div>
244
+ </div>
245
+ )}
246
+
247
+ <Popover.Arrow className="fill-border" />
248
+ </Popover.Content>
249
+ </Popover.Portal>
250
+ </Popover.Root>
251
+ );
252
+ }