codex-snapshots 0.1.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.
@@ -0,0 +1,92 @@
1
+ const DEFAULT_VIEWER_URL = "http://127.0.0.1:4321/";
2
+ const DEFAULT_API_URL = "http://127.0.0.1:8787";
3
+
4
+ const params = new URLSearchParams(window.location.search);
5
+ const viewerUrl = normalizeViewerUrl(params.get("viewer") || DEFAULT_VIEWER_URL);
6
+ const apiUrl = normalizeApiUrl(params.get("api") || localStorage.getItem("codex-snapshots.api") || DEFAULT_API_URL);
7
+
8
+ const viewerStatus = document.getElementById("viewer-status");
9
+ const apiStatus = document.getElementById("api-status");
10
+ const viewerLink = document.getElementById("open-local-viewer");
11
+ const viewerUrlLabel = document.getElementById("viewer-url-label");
12
+ const apiInput = document.getElementById("api-url");
13
+ const shareInput = document.getElementById("share-id");
14
+ const shareForm = document.getElementById("share-form");
15
+
16
+ viewerLink.href = viewerUrl;
17
+ viewerUrlLabel.textContent = viewerUrl;
18
+ apiInput.value = apiUrl;
19
+ shareInput.value = params.get("id") || "";
20
+
21
+ checkViewer(viewerUrl);
22
+ checkApi(apiUrl);
23
+
24
+ apiInput.addEventListener("change", () => {
25
+ localStorage.setItem("codex-snapshots.api", normalizeApiUrl(apiInput.value));
26
+ checkApi(normalizeApiUrl(apiInput.value));
27
+ });
28
+
29
+ shareForm.addEventListener("submit", (event) => {
30
+ event.preventDefault();
31
+ const id = shareInput.value.trim();
32
+ const api = normalizeApiUrl(apiInput.value);
33
+
34
+ if (!id) {
35
+ shareInput.focus();
36
+ return;
37
+ }
38
+
39
+ localStorage.setItem("codex-snapshots.api", api);
40
+ const target = new URL("./share/index.html", window.location.href);
41
+ target.searchParams.set("id", id);
42
+ target.searchParams.set("api", api);
43
+ window.location.href = target.toString();
44
+ });
45
+
46
+ async function checkViewer(url) {
47
+ setStatus(viewerStatus, "检查中", "checking");
48
+
49
+ try {
50
+ await fetch(url, {
51
+ cache: "no-store",
52
+ mode: "no-cors",
53
+ signal: AbortSignal.timeout(2500),
54
+ });
55
+ setStatus(viewerStatus, "已连接", "ready");
56
+ } catch {
57
+ setStatus(viewerStatus, "未启动", "error");
58
+ }
59
+ }
60
+
61
+ async function checkApi(url) {
62
+ setStatus(apiStatus, "检查中", "checking");
63
+
64
+ try {
65
+ const response = await fetch(`${url}/api/snapshots/health`, {
66
+ cache: "no-store",
67
+ signal: AbortSignal.timeout(2500),
68
+ });
69
+
70
+ if (!response.ok) {
71
+ throw new Error(`HTTP ${response.status}`);
72
+ }
73
+
74
+ setStatus(apiStatus, "已连接", "ready");
75
+ } catch {
76
+ setStatus(apiStatus, "可选", "error");
77
+ }
78
+ }
79
+
80
+ function setStatus(element, text, state) {
81
+ element.textContent = text;
82
+ element.className = `status-pill ${state}`;
83
+ }
84
+
85
+ function normalizeViewerUrl(value) {
86
+ const normalized = String(value || DEFAULT_VIEWER_URL).trim();
87
+ return normalized.endsWith("/") ? normalized : `${normalized}/`;
88
+ }
89
+
90
+ function normalizeApiUrl(value) {
91
+ return String(value || DEFAULT_API_URL).trim().replace(/\/+$/, "");
92
+ }
@@ -0,0 +1,97 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="description" content="面向 Codex、Claude Code 和 Trae 的本地优先、只读会话快照工具。">
7
+ <title>Codex Snapshots</title>
8
+ <link rel="stylesheet" href="./assets/site.css">
9
+ </head>
10
+ <body>
11
+ <main class="shell">
12
+ <section class="hero" aria-labelledby="hero-title">
13
+ <div class="hero-copy">
14
+ <p class="eyebrow">本地优先的 agent 审阅工具</p>
15
+ <h1 id="hero-title">Codex Snapshots</h1>
16
+ <p class="lede">
17
+ 从本地 Codex、Claude Code 和 Trae 会话中审阅、脱敏、导出并分享只读快照。
18
+ </p>
19
+ <div class="actions">
20
+ <a class="button primary" id="open-local-viewer" href="http://127.0.0.1:4321/">打开本地查看器</a>
21
+ <a class="button" href="https://github.com/ffffhx/codex-snapshots">GitHub 仓库</a>
22
+ </div>
23
+ </div>
24
+ <div class="product-shot" aria-label="Codex Snapshots 产品预览">
25
+ <div class="shot-sidebar">
26
+ <div class="shot-kicker">本地 Agents</div>
27
+ <div class="shot-title">项目</div>
28
+ <div class="shot-search"></div>
29
+ <div class="shot-row active"><span></span><b>garden-lab</b></div>
30
+ <div class="shot-row"><span></span><b>codex-snapshots</b></div>
31
+ <div class="shot-row"><span></span><b>coze-monorepo</b></div>
32
+ </div>
33
+ <div class="shot-main">
34
+ <div class="shot-top">
35
+ <div>
36
+ <div class="shot-kicker">只读审阅</div>
37
+ <div class="shot-heading">部署快照网站</div>
38
+ </div>
39
+ <div class="shot-switches"><span></span><span></span><span class="on"></span></div>
40
+ </div>
41
+ <div class="shot-meta">Codex | ~/Code/codex-snapshots | 已脱敏:是</div>
42
+ <div class="shot-bubble user">这个能变成一个独立项目吗?</div>
43
+ <div class="shot-bubble assistant">可以。它已经有独立的数据边界、查看器、分享 API 和部署入口。</div>
44
+ </div>
45
+ </div>
46
+ </section>
47
+
48
+ <section class="status-grid" aria-label="连接状态">
49
+ <article class="status-panel">
50
+ <div class="panel-heading">
51
+ <p class="eyebrow">本地服务</p>
52
+ <span class="status-pill checking" id="viewer-status">检查中</span>
53
+ </div>
54
+ <h2>打开你电脑上的只读查看器</h2>
55
+ <p>
56
+ 这个网站只是入口外壳。会话数据仍保留在你的电脑上,并从
57
+ <code id="viewer-url-label">http://127.0.0.1:4321/</code> 读取。
58
+ </p>
59
+ <pre><code>pnpm install
60
+ pnpm dev</code></pre>
61
+ </article>
62
+
63
+ <article class="status-panel">
64
+ <div class="panel-heading">
65
+ <p class="eyebrow">云端分享</p>
66
+ <span class="status-pill checking" id="api-status">检查中</span>
67
+ </div>
68
+ <h2>打开一个分享快照</h2>
69
+ <form class="share-form" id="share-form">
70
+ <label>
71
+ <span>分享 ID</span>
72
+ <input id="share-id" name="id" autocomplete="off" placeholder="snap_..." spellcheck="false">
73
+ </label>
74
+ <label>
75
+ <span>分享 API</span>
76
+ <input id="api-url" name="api" autocomplete="off" spellcheck="false">
77
+ </label>
78
+ <button class="button primary" type="submit">打开分享</button>
79
+ </form>
80
+ </article>
81
+ </section>
82
+
83
+ <section class="commands" aria-label="命令">
84
+ <div>
85
+ <p class="eyebrow">安装</p>
86
+ <h2>在本地运行</h2>
87
+ </div>
88
+ <pre><code>git clone https://github.com/ffffhx/codex-snapshots.git
89
+ cd codex-snapshots
90
+ pnpm install
91
+ pnpm dev</code></pre>
92
+ </section>
93
+ </main>
94
+
95
+ <script src="./assets/site.js"></script>
96
+ </body>
97
+ </html>
@@ -0,0 +1,22 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="description" content="查看一个已脱敏的 Codex Snapshots 云端分享。">
7
+ <title>快照分享 | Codex Snapshots</title>
8
+ <link rel="stylesheet" href="../assets/site.css">
9
+ </head>
10
+ <body>
11
+ <main class="share-shell">
12
+ <header class="share-header">
13
+ <p class="eyebrow">云端只读快照</p>
14
+ <h1 id="share-title">正在加载快照</h1>
15
+ <p id="share-meta" class="share-meta">正在等待分享元数据。</p>
16
+ </header>
17
+ <section id="share-content" class="turns" aria-live="polite">正在加载...</section>
18
+ </main>
19
+
20
+ <script src="../assets/share.js"></script>
21
+ </body>
22
+ </html>