@kimuson/claude-code-viewer 0.4.5 → 0.4.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.
package/dist/main.js CHANGED
@@ -6,7 +6,7 @@ import { resolve as resolve6 } from "node:path";
6
6
  import { NodeContext as NodeContext2 } from "@effect/platform-node";
7
7
  import { serve } from "@hono/node-server";
8
8
  import { serveStatic } from "@hono/node-server/serve-static";
9
- import { Effect as Effect38 } from "effect";
9
+ import { Effect as Effect39 } from "effect";
10
10
 
11
11
  // src/server/core/claude-code/presentation/ClaudeCodeController.ts
12
12
  import { FileSystem as FileSystem4, Path as Path6 } from "@effect/platform";
@@ -382,40 +382,70 @@ var FileHistorySnapshotEntrySchema = z11.object({
382
382
  });
383
383
 
384
384
  // src/lib/conversation-schema/entry/QueueOperationEntrySchema.ts
385
+ import { z as z13 } from "zod";
386
+
387
+ // src/lib/conversation-schema/content/DocumentContentSchema.ts
385
388
  import { z as z12 } from "zod";
386
- var QueueOperationEntrySchema = z12.union([
387
- z12.object({
388
- type: z12.literal("queue-operation"),
389
- operation: z12.literal("enqueue"),
390
- content: z12.string(),
391
- sessionId: z12.string(),
392
- timestamp: z12.iso.datetime()
389
+ var DocumentContentSchema = z12.object({
390
+ type: z12.literal("document"),
391
+ source: z12.union([
392
+ z12.object({
393
+ media_type: z12.literal("text/plain"),
394
+ type: z12.literal("text"),
395
+ data: z12.string()
396
+ }),
397
+ z12.object({
398
+ media_type: z12.enum(["application/pdf"]),
399
+ type: z12.literal("base64"),
400
+ data: z12.string()
401
+ })
402
+ ])
403
+ });
404
+
405
+ // src/lib/conversation-schema/entry/QueueOperationEntrySchema.ts
406
+ var QueueOperationContentSchema = z13.union([
407
+ z13.string(),
408
+ TextContentSchema,
409
+ ToolResultContentSchema,
410
+ ImageContentSchema,
411
+ DocumentContentSchema
412
+ ]);
413
+ var QueueOperationEntrySchema = z13.union([
414
+ z13.object({
415
+ type: z13.literal("queue-operation"),
416
+ operation: z13.literal("enqueue"),
417
+ content: z13.union([
418
+ z13.string(),
419
+ z13.array(z13.union([z13.string(), QueueOperationContentSchema]))
420
+ ]),
421
+ sessionId: z13.string(),
422
+ timestamp: z13.iso.datetime()
393
423
  }),
394
- z12.object({
395
- type: z12.literal("queue-operation"),
396
- operation: z12.literal("dequeue"),
397
- sessionId: z12.string(),
398
- timestamp: z12.iso.datetime()
424
+ z13.object({
425
+ type: z13.literal("queue-operation"),
426
+ operation: z13.literal("dequeue"),
427
+ sessionId: z13.string(),
428
+ timestamp: z13.iso.datetime()
399
429
  })
400
430
  ]);
401
431
 
402
432
  // src/lib/conversation-schema/entry/SummaryEntrySchema.ts
403
- import { z as z13 } from "zod";
404
- var SummaryEntrySchema = z13.object({
405
- type: z13.literal("summary"),
406
- summary: z13.string(),
407
- leafUuid: z13.string().uuid()
433
+ import { z as z14 } from "zod";
434
+ var SummaryEntrySchema = z14.object({
435
+ type: z14.literal("summary"),
436
+ summary: z14.string(),
437
+ leafUuid: z14.string().uuid()
408
438
  });
409
439
 
410
440
  // src/lib/conversation-schema/entry/SystemEntrySchema.ts
411
- import { z as z14 } from "zod";
441
+ import { z as z15 } from "zod";
412
442
  var SystemEntrySchema = BaseEntrySchema.extend({
413
443
  // discriminator
414
- type: z14.literal("system"),
444
+ type: z15.literal("system"),
415
445
  // required
416
- content: z14.string(),
417
- toolUseID: z14.string(),
418
- level: z14.enum(["info"])
446
+ content: z15.string(),
447
+ toolUseID: z15.string(),
448
+ level: z15.enum(["info"])
419
449
  });
420
450
 
421
451
  // src/lib/conversation-schema/entry/UserEntrySchema.ts
@@ -423,26 +453,6 @@ import { z as z17 } from "zod";
423
453
 
424
454
  // src/lib/conversation-schema/message/UserMessageSchema.ts
425
455
  import { z as z16 } from "zod";
426
-
427
- // src/lib/conversation-schema/content/DocumentContentSchema.ts
428
- import { z as z15 } from "zod";
429
- var DocumentContentSchema = z15.object({
430
- type: z15.literal("document"),
431
- source: z15.union([
432
- z15.object({
433
- media_type: z15.literal("text/plain"),
434
- type: z15.literal("text"),
435
- data: z15.string()
436
- }),
437
- z15.object({
438
- media_type: z15.enum(["application/pdf"]),
439
- type: z15.literal("base64"),
440
- data: z15.string()
441
- })
442
- ])
443
- });
444
-
445
- // src/lib/conversation-schema/message/UserMessageSchema.ts
446
456
  var UserMessageContentSchema = z16.union([
447
457
  z16.string(),
448
458
  TextContentSchema,
@@ -4980,10 +4990,791 @@ var SchedulerController = class extends Context28.Tag("SchedulerController")() {
4980
4990
  };
4981
4991
 
4982
4992
  // src/server/core/session/presentation/SessionController.ts
4983
- import { Context as Context29, Effect as Effect34, Layer as Layer30 } from "effect";
4984
- var LayerImpl23 = Effect34.gen(function* () {
4993
+ import { Context as Context29, Effect as Effect35, Layer as Layer30 } from "effect";
4994
+
4995
+ // src/server/core/session/services/ExportService.ts
4996
+ import { Effect as Effect34 } from "effect";
4997
+ var escapeHtml = (text) => {
4998
+ const map = {
4999
+ "&": "&",
5000
+ "<": "&lt;",
5001
+ ">": "&gt;",
5002
+ '"': "&quot;",
5003
+ "'": "&#039;"
5004
+ };
5005
+ return text.replace(/[&<>"']/g, (char) => map[char] ?? char);
5006
+ };
5007
+ var formatJsonWithNewlines = (obj) => {
5008
+ const jsonString = JSON.stringify(obj, null, 2);
5009
+ return jsonString.replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\r/g, "\r");
5010
+ };
5011
+ var formatTimestamp = (timestamp) => {
5012
+ const date = new Date(timestamp);
5013
+ return date.toLocaleString("en-US", {
5014
+ year: "numeric",
5015
+ month: "short",
5016
+ day: "numeric",
5017
+ hour: "2-digit",
5018
+ minute: "2-digit",
5019
+ second: "2-digit"
5020
+ });
5021
+ };
5022
+ var renderMarkdown = (content) => {
5023
+ let html = escapeHtml(content);
5024
+ html = html.replace(
5025
+ /```(\w+)?\n([\s\S]*?)```/g,
5026
+ (_match, lang, code) => `
5027
+ <div class="code-block">
5028
+ ${lang ? `<div class="code-header"><span class="code-lang">${escapeHtml(lang.toUpperCase())}</span></div>` : ""}
5029
+ <pre><code class="language-${escapeHtml(lang || "text")}">${code.trim()}</code></pre>
5030
+ </div>
5031
+ `
5032
+ );
5033
+ html = html.replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>');
5034
+ html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
5035
+ html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
5036
+ html = html.replace(/^### (.+)$/gm, '<h3 class="markdown-h3">$1</h3>');
5037
+ html = html.replace(/^## (.+)$/gm, '<h2 class="markdown-h2">$1</h2>');
5038
+ html = html.replace(/^# (.+)$/gm, '<h1 class="markdown-h1">$1</h1>');
5039
+ html = html.replace(
5040
+ /\[([^\]]+)\]\(([^)]+)\)/g,
5041
+ '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
5042
+ );
5043
+ html = html.split("\n\n").map((para) => {
5044
+ if (para.startsWith("<h") || para.startsWith("<div") || para.startsWith("<pre") || para.trim() === "") {
5045
+ return para;
5046
+ }
5047
+ return `<p class="markdown-p">${para.replace(/\n/g, "<br>")}</p>`;
5048
+ }).join("\n");
5049
+ return html;
5050
+ };
5051
+ var renderUserEntry = (entry) => {
5052
+ const contentArray = Array.isArray(entry.message.content) ? entry.message.content : [entry.message.content];
5053
+ const contentHtml = contentArray.map((msg) => {
5054
+ if (typeof msg === "string") {
5055
+ return `<div class="markdown-content">${renderMarkdown(msg)}</div>`;
5056
+ }
5057
+ if (msg.type === "text") {
5058
+ return `<div class="markdown-content">${renderMarkdown(msg.text)}</div>`;
5059
+ }
5060
+ if (msg.type === "image") {
5061
+ return `<img src="data:${msg.source.media_type};base64,${msg.source.data}" alt="User uploaded image" class="message-image" />`;
5062
+ }
5063
+ if (msg.type === "document") {
5064
+ return `<div class="document-content"><strong>Document:</strong> ${escapeHtml(msg.source.media_type)}</div>`;
5065
+ }
5066
+ if (msg.type === "tool_result") {
5067
+ return "";
5068
+ }
5069
+ return "";
5070
+ }).join("");
5071
+ if (!contentHtml.trim()) {
5072
+ return "";
5073
+ }
5074
+ return `
5075
+ <div class="conversation-entry user-entry">
5076
+ <div class="entry-header">
5077
+ <span class="entry-role">User</span>
5078
+ <span class="entry-timestamp">${formatTimestamp(entry.timestamp)}</span>
5079
+ </div>
5080
+ <div class="entry-content">
5081
+ ${contentHtml}
5082
+ </div>
5083
+ </div>
5084
+ `;
5085
+ };
5086
+ var renderAssistantEntry = (entry) => {
5087
+ const contentHtml = entry.message.content.map((msg) => {
5088
+ if (msg.type === "text") {
5089
+ return `<div class="markdown-content">${renderMarkdown(msg.text)}</div>`;
5090
+ }
5091
+ if (msg.type === "thinking") {
5092
+ const charCount = msg.thinking.length;
5093
+ return `
5094
+ <div class="thinking-block collapsible">
5095
+ <div class="thinking-header collapsible-trigger">
5096
+ <svg class="icon-lightbulb" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
5097
+ <path d="M12 2v1m0 18v1m9-10h1M2 12H1m17.66-7.66l.71.71M3.63 20.37l.71.71m0-14.14l-.71.71m17.02 12.73l-.71.71M12 7a5 5 0 0 1 5 5 5 5 0 0 1-1.47 3.53c-.6.6-.94 1.42-.94 2.27V18a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1v-.2c0-.85-.34-1.67-.94-2.27A5 5 0 0 1 7 12a5 5 0 0 1 5-5Z"/>
5098
+ </svg>
5099
+ <span class="thinking-title">Thinking</span>
5100
+ <span class="expand-hint">(${charCount} characters \xB7 click to collapse)</span>
5101
+ <svg class="icon-chevron" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
5102
+ <polyline points="6 9 12 15 18 9"></polyline>
5103
+ </svg>
5104
+ </div>
5105
+ <div class="thinking-content collapsible-content">
5106
+ <pre class="thinking-text">${escapeHtml(msg.thinking)}</pre>
5107
+ </div>
5108
+ </div>
5109
+ `;
5110
+ }
5111
+ if (msg.type === "tool_use") {
5112
+ const inputKeys = Object.keys(msg.input).length;
5113
+ return `
5114
+ <div class="tool-use-block collapsible">
5115
+ <div class="tool-use-header collapsible-trigger">
5116
+ <svg class="icon-wrench" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
5117
+ <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
5118
+ </svg>
5119
+ <span class="tool-name">${escapeHtml(msg.name)}</span>
5120
+ <span class="expand-hint">(${inputKeys} parameter${inputKeys !== 1 ? "s" : ""} \xB7 click to collapse)</span>
5121
+ <svg class="icon-chevron" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
5122
+ <polyline points="6 9 12 15 18 9"></polyline>
5123
+ </svg>
5124
+ </div>
5125
+ <div class="tool-use-content collapsible-content">
5126
+ <div class="tool-id"><strong>Tool ID:</strong> <code>${escapeHtml(msg.id)}</code></div>
5127
+ <div class="tool-input">
5128
+ <strong>Input Parameters:</strong>
5129
+ <pre class="json-input">${escapeHtml(formatJsonWithNewlines(msg.input))}</pre>
5130
+ </div>
5131
+ </div>
5132
+ </div>
5133
+ `;
5134
+ }
5135
+ return "";
5136
+ }).join("");
5137
+ return `
5138
+ <div class="conversation-entry assistant-entry">
5139
+ <div class="entry-header">
5140
+ <span class="entry-role">Assistant</span>
5141
+ <span class="entry-timestamp">${formatTimestamp(entry.timestamp)}</span>
5142
+ </div>
5143
+ <div class="entry-content">
5144
+ ${contentHtml}
5145
+ </div>
5146
+ </div>
5147
+ `;
5148
+ };
5149
+ var renderSystemEntry = (entry) => {
5150
+ return `
5151
+ <div class="conversation-entry system-entry">
5152
+ <div class="entry-header">
5153
+ <span class="entry-role">System</span>
5154
+ <span class="entry-timestamp">${formatTimestamp(entry.timestamp)}</span>
5155
+ </div>
5156
+ <div class="entry-content">
5157
+ <div class="system-message">${escapeHtml(entry.content)}</div>
5158
+ </div>
5159
+ </div>
5160
+ `;
5161
+ };
5162
+ var groupConsecutiveAssistantMessages = (conversations) => {
5163
+ const grouped = [];
5164
+ let currentGroup = [];
5165
+ for (const conv of conversations) {
5166
+ if (conv.type === "assistant") {
5167
+ currentGroup.push(conv);
5168
+ } else if (conv.type === "user" || conv.type === "system") {
5169
+ if (currentGroup.length > 0) {
5170
+ grouped.push({
5171
+ type: currentGroup.length > 1 ? "grouped" : "single",
5172
+ entries: currentGroup
5173
+ });
5174
+ currentGroup = [];
5175
+ }
5176
+ grouped.push({ type: "single", entries: [conv] });
5177
+ }
5178
+ }
5179
+ if (currentGroup.length > 0) {
5180
+ grouped.push({
5181
+ type: currentGroup.length > 1 ? "grouped" : "single",
5182
+ entries: currentGroup
5183
+ });
5184
+ }
5185
+ return grouped;
5186
+ };
5187
+ var renderGroupedAssistantEntries = (entries) => {
5188
+ const allContent = entries.flatMap((entry) => entry.message.content);
5189
+ const firstEntry = entries[0];
5190
+ if (!firstEntry) {
5191
+ return "";
5192
+ }
5193
+ const contentHtml = allContent.map((msg) => {
5194
+ if (msg.type === "text") {
5195
+ return `<div class="markdown-content">${renderMarkdown(msg.text)}</div>`;
5196
+ }
5197
+ if (msg.type === "thinking") {
5198
+ const charCount = msg.thinking.length;
5199
+ return `
5200
+ <div class="thinking-block collapsible">
5201
+ <div class="thinking-header collapsible-trigger">
5202
+ <svg class="icon-lightbulb" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
5203
+ <path d="M12 2v1m0 18v1m9-10h1M2 12H1m17.66-7.66l.71.71M3.63 20.37l.71.71m0-14.14l-.71.71m17.02 12.73l-.71.71M12 7a5 5 0 0 1 5 5 5 5 0 0 1-1.47 3.53c-.6.6-.94 1.42-.94 2.27V18a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1v-.2c0-.85-.34-1.67-.94-2.27A5 5 0 0 1 7 12a5 5 0 0 1 5-5Z"/>
5204
+ </svg>
5205
+ <span class="thinking-title">Thinking</span>
5206
+ <span class="expand-hint">(${charCount} characters \xB7 click to collapse)</span>
5207
+ <svg class="icon-chevron" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
5208
+ <polyline points="6 9 12 15 18 9"></polyline>
5209
+ </svg>
5210
+ </div>
5211
+ <div class="thinking-content collapsible-content">
5212
+ <pre class="thinking-text">${escapeHtml(msg.thinking)}</pre>
5213
+ </div>
5214
+ </div>
5215
+ `;
5216
+ }
5217
+ if (msg.type === "tool_use") {
5218
+ const inputKeys = Object.keys(msg.input).length;
5219
+ return `
5220
+ <div class="tool-use-block collapsible">
5221
+ <div class="tool-use-header collapsible-trigger">
5222
+ <svg class="icon-wrench" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
5223
+ <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
5224
+ </svg>
5225
+ <span class="tool-name">${escapeHtml(msg.name)}</span>
5226
+ <span class="expand-hint">(${inputKeys} parameter${inputKeys !== 1 ? "s" : ""} \xB7 click to collapse)</span>
5227
+ <svg class="icon-chevron" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
5228
+ <polyline points="6 9 12 15 18 9"></polyline>
5229
+ </svg>
5230
+ </div>
5231
+ <div class="tool-use-content collapsible-content">
5232
+ <div class="tool-id"><strong>Tool ID:</strong> <code>${escapeHtml(msg.id)}</code></div>
5233
+ <div class="tool-input">
5234
+ <strong>Input Parameters:</strong>
5235
+ <pre class="json-input">${escapeHtml(formatJsonWithNewlines(msg.input))}</pre>
5236
+ </div>
5237
+ </div>
5238
+ </div>
5239
+ `;
5240
+ }
5241
+ return "";
5242
+ }).join("");
5243
+ return `
5244
+ <div class="conversation-entry assistant-entry">
5245
+ <div class="entry-header">
5246
+ <span class="entry-role">Assistant</span>
5247
+ <span class="entry-timestamp">${formatTimestamp(firstEntry.timestamp)}</span>
5248
+ </div>
5249
+ <div class="entry-content">
5250
+ ${contentHtml}
5251
+ </div>
5252
+ </div>
5253
+ `;
5254
+ };
5255
+ var generateSessionHtml = (session, projectId) => Effect34.gen(function* () {
5256
+ const grouped = groupConsecutiveAssistantMessages(session.conversations);
5257
+ const conversationsHtml = grouped.map((group) => {
5258
+ if (group.type === "grouped") {
5259
+ return renderGroupedAssistantEntries(
5260
+ group.entries
5261
+ );
5262
+ }
5263
+ const conv = group.entries[0];
5264
+ if (!conv) {
5265
+ return "";
5266
+ }
5267
+ if (conv.type === "user") {
5268
+ return renderUserEntry(conv);
5269
+ }
5270
+ if (conv.type === "assistant") {
5271
+ return renderAssistantEntry(conv);
5272
+ }
5273
+ if (conv.type === "system") {
5274
+ return renderSystemEntry(conv);
5275
+ }
5276
+ return "";
5277
+ }).filter((html2) => html2 !== "").join("\n");
5278
+ const html = `<!DOCTYPE html>
5279
+ <html lang="en">
5280
+ <head>
5281
+ <meta charset="UTF-8">
5282
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
5283
+ <title>Claude Code Session - ${escapeHtml(session.id)}</title>
5284
+ <style>
5285
+ * {
5286
+ margin: 0;
5287
+ padding: 0;
5288
+ box-sizing: border-box;
5289
+ }
5290
+
5291
+ :root {
5292
+ --background: 0 0% 100%;
5293
+ --foreground: 0 0% 3.9%;
5294
+ --muted: 0 0% 96.1%;
5295
+ --muted-foreground: 0 0% 45.1%;
5296
+ --border: 0 0% 89.8%;
5297
+ --primary: 0 0% 9%;
5298
+ --blue-50: 214 100% 97%;
5299
+ --blue-200: 213 97% 87%;
5300
+ --blue-600: 217 91% 60%;
5301
+ --blue-800: 217 91% 35%;
5302
+ }
5303
+
5304
+ body {
5305
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
5306
+ line-height: 1.6;
5307
+ color: hsl(var(--foreground));
5308
+ background: hsl(var(--background));
5309
+ padding: 2rem;
5310
+ max-width: 1200px;
5311
+ margin: 0 auto;
5312
+ }
5313
+
5314
+ .header {
5315
+ border-bottom: 1px solid hsl(var(--border));
5316
+ padding-bottom: 2rem;
5317
+ margin-bottom: 2rem;
5318
+ }
5319
+
5320
+ .header h1 {
5321
+ font-size: 2rem;
5322
+ font-weight: 700;
5323
+ margin-bottom: 0.5rem;
5324
+ }
5325
+
5326
+ .header .metadata {
5327
+ color: hsl(var(--muted-foreground));
5328
+ font-size: 0.875rem;
5329
+ }
5330
+
5331
+ .conversation-list {
5332
+ display: flex;
5333
+ flex-direction: column;
5334
+ gap: 1.5rem;
5335
+ }
5336
+
5337
+ .conversation-entry {
5338
+ border-radius: 0.5rem;
5339
+ overflow: hidden;
5340
+ }
5341
+
5342
+ .entry-header {
5343
+ display: flex;
5344
+ justify-content: space-between;
5345
+ align-items: center;
5346
+ padding: 0.75rem 1rem;
5347
+ font-size: 0.875rem;
5348
+ font-weight: 500;
5349
+ border-bottom: 1px solid;
5350
+ }
5351
+
5352
+ .entry-timestamp {
5353
+ color: hsl(var(--muted-foreground));
5354
+ font-size: 0.75rem;
5355
+ }
5356
+
5357
+ .entry-content {
5358
+ padding: 1.5rem;
5359
+ }
5360
+
5361
+ /* User entry styles */
5362
+ .user-entry {
5363
+ background: hsl(var(--muted) / 0.3);
5364
+ border: 1px solid hsl(var(--border));
5365
+ }
5366
+
5367
+ .user-entry .entry-header {
5368
+ background: hsl(var(--muted) / 0.5);
5369
+ border-bottom-color: hsl(var(--border));
5370
+ }
5371
+
5372
+ /* Assistant entry styles */
5373
+ .assistant-entry {
5374
+ background: hsl(var(--background));
5375
+ border: 1px solid hsl(var(--border));
5376
+ }
5377
+
5378
+ .assistant-entry .entry-header {
5379
+ background: hsl(var(--muted) / 0.3);
5380
+ border-bottom-color: hsl(var(--border));
5381
+ }
5382
+
5383
+ /* System entry styles */
5384
+ .system-entry {
5385
+ background: hsl(var(--muted) / 0.2);
5386
+ border: 1px dashed hsl(var(--border));
5387
+ }
5388
+
5389
+ .system-entry .entry-header {
5390
+ background: hsl(var(--muted) / 0.4);
5391
+ border-bottom-color: hsl(var(--border));
5392
+ }
5393
+
5394
+ .system-message {
5395
+ font-family: monospace;
5396
+ font-size: 0.875rem;
5397
+ color: hsl(var(--muted-foreground));
5398
+ }
5399
+
5400
+ /* Markdown styles */
5401
+ .markdown-content {
5402
+ width: 100%;
5403
+ margin: 1rem 0.25rem;
5404
+ }
5405
+
5406
+ .markdown-h1 {
5407
+ font-size: 1.875rem;
5408
+ font-weight: 700;
5409
+ margin-bottom: 1.5rem;
5410
+ margin-top: 2rem;
5411
+ padding-bottom: 0.75rem;
5412
+ border-bottom: 1px solid hsl(var(--border));
5413
+ }
5414
+
5415
+ .markdown-h2 {
5416
+ font-size: 1.5rem;
5417
+ font-weight: 600;
5418
+ margin-bottom: 1rem;
5419
+ margin-top: 2rem;
5420
+ padding-bottom: 0.5rem;
5421
+ border-bottom: 1px solid hsl(var(--border) / 0.5);
5422
+ }
5423
+
5424
+ .markdown-h3 {
5425
+ font-size: 1.25rem;
5426
+ font-weight: 600;
5427
+ margin-bottom: 0.75rem;
5428
+ margin-top: 1.5rem;
5429
+ }
5430
+
5431
+ .markdown-p {
5432
+ margin-bottom: 1rem;
5433
+ line-height: 1.75;
5434
+ word-break: break-all;
5435
+ }
5436
+
5437
+ .inline-code {
5438
+ background: hsl(var(--muted) / 0.7);
5439
+ padding: 0.25rem 0.5rem;
5440
+ border-radius: 0.375rem;
5441
+ font-size: 0.875rem;
5442
+ font-family: monospace;
5443
+ border: 1px solid hsl(var(--border));
5444
+ }
5445
+
5446
+ .code-block {
5447
+ position: relative;
5448
+ margin: 1.5rem 0;
5449
+ }
5450
+
5451
+ .code-header {
5452
+ display: flex;
5453
+ align-items: center;
5454
+ justify-content: space-between;
5455
+ background: hsl(var(--muted) / 0.3);
5456
+ padding: 0.5rem 1rem;
5457
+ border-bottom: 1px solid hsl(var(--border));
5458
+ border-top-left-radius: 0.5rem;
5459
+ border-top-right-radius: 0.5rem;
5460
+ border: 1px solid hsl(var(--border));
5461
+ border-bottom: none;
5462
+ }
5463
+
5464
+ .code-lang {
5465
+ font-size: 0.75rem;
5466
+ font-weight: 500;
5467
+ color: hsl(var(--muted-foreground));
5468
+ text-transform: uppercase;
5469
+ letter-spacing: 0.05em;
5470
+ }
5471
+
5472
+ .code-block pre {
5473
+ margin: 0;
5474
+ padding: 1rem;
5475
+ background: hsl(var(--muted) / 0.2);
5476
+ border: 1px solid hsl(var(--border));
5477
+ border-top: none;
5478
+ border-bottom-left-radius: 0.5rem;
5479
+ border-bottom-right-radius: 0.5rem;
5480
+ overflow-x: auto;
5481
+ }
5482
+
5483
+ .code-block code {
5484
+ font-family: 'Monaco', 'Courier New', monospace;
5485
+ font-size: 0.875rem;
5486
+ line-height: 1.5;
5487
+ }
5488
+
5489
+ /* Thinking block styles */
5490
+ .thinking-block {
5491
+ background: hsl(var(--muted) / 0.5);
5492
+ border: 2px dashed hsl(var(--border));
5493
+ border-radius: 0.5rem;
5494
+ margin-bottom: 0.5rem;
5495
+ overflow: hidden;
5496
+ }
5497
+
5498
+ .thinking-header {
5499
+ display: flex;
5500
+ align-items: center;
5501
+ gap: 0.5rem;
5502
+ padding: 0.75rem 1rem;
5503
+ cursor: pointer;
5504
+ background: hsl(var(--muted) / 0.3);
5505
+ transition: background 0.2s;
5506
+ }
5507
+
5508
+ .thinking-header:hover {
5509
+ background: hsl(var(--muted) / 0.5);
5510
+ }
5511
+
5512
+ .icon-lightbulb {
5513
+ color: hsl(var(--muted-foreground));
5514
+ flex-shrink: 0;
5515
+ }
5516
+
5517
+ .thinking-title {
5518
+ font-size: 0.875rem;
5519
+ font-weight: 500;
5520
+ }
5521
+
5522
+ .expand-hint {
5523
+ font-size: 0.75rem;
5524
+ color: hsl(var(--muted-foreground));
5525
+ font-weight: normal;
5526
+ margin-left: 0.5rem;
5527
+ }
5528
+
5529
+ .collapsible:not(.collapsed) .expand-hint {
5530
+ display: none;
5531
+ }
5532
+
5533
+ .icon-chevron {
5534
+ margin-left: auto;
5535
+ color: hsl(var(--muted-foreground));
5536
+ transition: transform 0.2s;
5537
+ }
5538
+
5539
+ .collapsible.collapsed .icon-chevron {
5540
+ transform: rotate(-90deg);
5541
+ }
5542
+
5543
+ .thinking-content {
5544
+ padding: 0.5rem 1rem;
5545
+ }
5546
+
5547
+ .collapsible-content {
5548
+ max-height: 1000px;
5549
+ overflow: hidden;
5550
+ transition: max-height 0.3s ease-out, opacity 0.2s ease-out;
5551
+ }
5552
+
5553
+ .collapsible.collapsed .collapsible-content {
5554
+ max-height: 0;
5555
+ opacity: 0;
5556
+ }
5557
+
5558
+ .thinking-text {
5559
+ font-size: 0.875rem;
5560
+ color: hsl(var(--muted-foreground));
5561
+ font-family: monospace;
5562
+ white-space: pre-wrap;
5563
+ word-break: break-word;
5564
+ }
5565
+
5566
+ /* Tool use block styles */
5567
+ .tool-use-block {
5568
+ border: 1px solid hsl(var(--blue-200));
5569
+ background: hsl(var(--blue-50) / 0.5);
5570
+ border-radius: 0.5rem;
5571
+ margin-bottom: 0.5rem;
5572
+ overflow: hidden;
5573
+ }
5574
+
5575
+ .tool-use-header {
5576
+ display: flex;
5577
+ align-items: center;
5578
+ gap: 0.5rem;
5579
+ padding: 0.375rem 0.75rem;
5580
+ cursor: pointer;
5581
+ background: hsl(var(--blue-50) / 0.3);
5582
+ transition: background 0.2s;
5583
+ }
5584
+
5585
+ .tool-use-header:hover {
5586
+ background: hsl(var(--blue-50) / 0.6);
5587
+ }
5588
+
5589
+ .icon-wrench {
5590
+ color: hsl(var(--blue-600));
5591
+ flex-shrink: 0;
5592
+ }
5593
+
5594
+ .tool-name {
5595
+ font-size: 0.875rem;
5596
+ font-weight: 500;
5597
+ flex: 1;
5598
+ overflow: hidden;
5599
+ text-overflow: ellipsis;
5600
+ white-space: nowrap;
5601
+ }
5602
+
5603
+ .tool-use-content {
5604
+ padding: 0.75rem 1rem;
5605
+ border-top: 1px solid hsl(var(--blue-200));
5606
+ display: flex;
5607
+ flex-direction: column;
5608
+ gap: 0.75rem;
5609
+ }
5610
+
5611
+ .tool-id {
5612
+ font-size: 0.75rem;
5613
+ }
5614
+
5615
+ .tool-id code {
5616
+ background: hsl(var(--background) / 0.5);
5617
+ padding: 0.25rem 0.5rem;
5618
+ border-radius: 0.25rem;
5619
+ border: 1px solid hsl(var(--blue-200));
5620
+ font-family: monospace;
5621
+ font-size: 0.75rem;
5622
+ }
5623
+
5624
+ .tool-input {
5625
+ font-size: 0.75rem;
5626
+ }
5627
+
5628
+ .json-input {
5629
+ background: hsl(var(--background));
5630
+ border: 1px solid hsl(var(--border));
5631
+ border-radius: 0.375rem;
5632
+ padding: 0.75rem;
5633
+ margin-top: 0.5rem;
5634
+ overflow-x: auto;
5635
+ font-family: monospace;
5636
+ font-size: 0.75rem;
5637
+ white-space: pre-wrap;
5638
+ word-break: break-all;
5639
+ overflow-wrap: break-word;
5640
+ }
5641
+
5642
+ .message-image {
5643
+ max-width: 100%;
5644
+ height: auto;
5645
+ border-radius: 0.5rem;
5646
+ margin: 1rem 0;
5647
+ }
5648
+
5649
+ strong {
5650
+ font-weight: 600;
5651
+ }
5652
+
5653
+ em {
5654
+ font-style: italic;
5655
+ }
5656
+
5657
+ a {
5658
+ color: hsl(var(--primary));
5659
+ text-decoration: underline;
5660
+ text-decoration-color: hsl(var(--primary) / 0.3);
5661
+ text-underline-offset: 4px;
5662
+ transition: text-decoration-color 0.2s;
5663
+ }
5664
+
5665
+ a:hover {
5666
+ text-decoration-color: hsl(var(--primary) / 0.6);
5667
+ }
5668
+
5669
+ .header-top {
5670
+ display: flex;
5671
+ justify-content: space-between;
5672
+ align-items: center;
5673
+ margin-bottom: 1rem;
5674
+ }
5675
+
5676
+ .toggle-all-button {
5677
+ padding: 0.5rem 1rem;
5678
+ background: hsl(var(--primary));
5679
+ color: white;
5680
+ border: none;
5681
+ border-radius: 0.375rem;
5682
+ font-size: 0.875rem;
5683
+ font-weight: 500;
5684
+ cursor: pointer;
5685
+ transition: opacity 0.2s;
5686
+ }
5687
+
5688
+ .toggle-all-button:hover {
5689
+ opacity: 0.9;
5690
+ }
5691
+
5692
+ .toggle-all-button:active {
5693
+ opacity: 0.8;
5694
+ }
5695
+
5696
+ .footer {
5697
+ margin-top: 4rem;
5698
+ padding-top: 2rem;
5699
+ border-top: 1px solid hsl(var(--border));
5700
+ text-align: center;
5701
+ color: hsl(var(--muted-foreground));
5702
+ font-size: 0.875rem;
5703
+ }
5704
+ </style>
5705
+ </head>
5706
+ <body>
5707
+ <div class="header">
5708
+ <div class="header-top">
5709
+ <h1>Claude Code Session Export</h1>
5710
+ <button id="toggle-all-btn" class="toggle-all-button">Collapse All</button>
5711
+ </div>
5712
+ <div class="metadata">
5713
+ <div><strong>Session ID:</strong> ${escapeHtml(session.id)}</div>
5714
+ <div><strong>Project ID:</strong> ${escapeHtml(projectId)}</div>
5715
+ <div><strong>Exported:</strong> ${formatTimestamp(Date.now())}</div>
5716
+ <div><strong>Total Conversations:</strong> ${session.conversations.length}</div>
5717
+ </div>
5718
+ </div>
5719
+
5720
+ <div class="conversation-list">
5721
+ ${conversationsHtml}
5722
+ </div>
5723
+
5724
+ <div class="footer">
5725
+ <p>Exported from Claude Code Viewer</p>
5726
+ </div>
5727
+
5728
+ <script>
5729
+ // Add click handlers for collapsible blocks
5730
+ document.addEventListener('DOMContentLoaded', function() {
5731
+ const triggers = document.querySelectorAll('.collapsible-trigger');
5732
+ const toggleAllBtn = document.getElementById('toggle-all-btn');
5733
+ let allExpanded = true; // Start as expanded since blocks are expanded by default
5734
+
5735
+ // Individual collapsible click handlers
5736
+ triggers.forEach(function(trigger) {
5737
+ trigger.addEventListener('click', function() {
5738
+ const collapsible = this.closest('.collapsible');
5739
+ if (collapsible) {
5740
+ collapsible.classList.toggle('collapsed');
5741
+ }
5742
+ });
5743
+ });
5744
+
5745
+ // Toggle all button
5746
+ if (toggleAllBtn) {
5747
+ toggleAllBtn.addEventListener('click', function() {
5748
+ const collapsibles = document.querySelectorAll('.collapsible');
5749
+
5750
+ if (allExpanded) {
5751
+ // Collapse all
5752
+ collapsibles.forEach(function(collapsible) {
5753
+ collapsible.classList.add('collapsed');
5754
+ });
5755
+ toggleAllBtn.textContent = 'Expand All';
5756
+ allExpanded = false;
5757
+ } else {
5758
+ // Expand all
5759
+ collapsibles.forEach(function(collapsible) {
5760
+ collapsible.classList.remove('collapsed');
5761
+ });
5762
+ toggleAllBtn.textContent = 'Collapse All';
5763
+ allExpanded = true;
5764
+ }
5765
+ });
5766
+ }
5767
+ });
5768
+ </script>
5769
+ </body>
5770
+ </html>`;
5771
+ return html;
5772
+ });
5773
+
5774
+ // src/server/core/session/presentation/SessionController.ts
5775
+ var LayerImpl23 = Effect35.gen(function* () {
4985
5776
  const sessionRepository = yield* SessionRepository;
4986
- const getSession = (options) => Effect34.gen(function* () {
5777
+ const getSession = (options) => Effect35.gen(function* () {
4987
5778
  const { projectId, sessionId } = options;
4988
5779
  const { session } = yield* sessionRepository.getSession(
4989
5780
  projectId,
@@ -4994,8 +5785,27 @@ var LayerImpl23 = Effect34.gen(function* () {
4994
5785
  response: { session }
4995
5786
  };
4996
5787
  });
5788
+ const exportSessionHtml = (options) => Effect35.gen(function* () {
5789
+ const { projectId, sessionId } = options;
5790
+ const { session } = yield* sessionRepository.getSession(
5791
+ projectId,
5792
+ sessionId
5793
+ );
5794
+ if (session === null) {
5795
+ return {
5796
+ status: 404,
5797
+ response: { error: "Session not found" }
5798
+ };
5799
+ }
5800
+ const html = yield* generateSessionHtml(session, projectId);
5801
+ return {
5802
+ status: 200,
5803
+ response: { html }
5804
+ };
5805
+ });
4997
5806
  return {
4998
- getSession
5807
+ getSession,
5808
+ exportSessionHtml
4999
5809
  };
5000
5810
  });
5001
5811
  var SessionController = class extends Context29.Tag("SessionController")() {
@@ -5009,12 +5819,12 @@ import { Hono } from "hono";
5009
5819
  var honoApp = new Hono();
5010
5820
 
5011
5821
  // src/server/hono/initialize.ts
5012
- import { Context as Context30, Effect as Effect35, Layer as Layer31, Ref as Ref11, Schedule as Schedule2 } from "effect";
5822
+ import { Context as Context30, Effect as Effect36, Layer as Layer31, Ref as Ref11, Schedule as Schedule2 } from "effect";
5013
5823
  var InitializeService = class extends Context30.Tag("InitializeService")() {
5014
5824
  static {
5015
5825
  this.Live = Layer31.effect(
5016
5826
  this,
5017
- Effect35.gen(function* () {
5827
+ Effect36.gen(function* () {
5018
5828
  const eventBus = yield* EventBus;
5019
5829
  const fileWatcher = yield* FileWatcherService;
5020
5830
  const projectRepository = yield* ProjectRepository;
@@ -5024,20 +5834,20 @@ var InitializeService = class extends Context30.Tag("InitializeService")() {
5024
5834
  const virtualConversationDatabase = yield* VirtualConversationDatabase;
5025
5835
  const listenersRef = yield* Ref11.make({});
5026
5836
  const startInitialization = () => {
5027
- return Effect35.gen(function* () {
5837
+ return Effect36.gen(function* () {
5028
5838
  yield* fileWatcher.startWatching();
5029
- const daemon = Effect35.repeat(
5839
+ const daemon = Effect36.repeat(
5030
5840
  eventBus.emit("heartbeat", {}),
5031
5841
  Schedule2.fixed("10 seconds")
5032
5842
  );
5033
5843
  console.log("start heartbeat");
5034
- yield* Effect35.forkDaemon(daemon);
5844
+ yield* Effect36.forkDaemon(daemon);
5035
5845
  console.log("after starting heartbeat fork");
5036
5846
  const onSessionChanged = (event) => {
5037
- Effect35.runFork(
5847
+ Effect36.runFork(
5038
5848
  projectMetaService.invalidateProject(event.projectId)
5039
5849
  );
5040
- Effect35.runFork(
5850
+ Effect36.runFork(
5041
5851
  sessionMetaService.invalidateSession(
5042
5852
  event.projectId,
5043
5853
  event.sessionId
@@ -5046,7 +5856,7 @@ var InitializeService = class extends Context30.Tag("InitializeService")() {
5046
5856
  };
5047
5857
  const onSessionProcessChanged = (event) => {
5048
5858
  if ((event.changed.type === "completed" || event.changed.type === "paused") && event.changed.sessionId !== void 0) {
5049
- Effect35.runFork(
5859
+ Effect36.runFork(
5050
5860
  virtualConversationDatabase.deleteVirtualConversations(
5051
5861
  event.changed.sessionId
5052
5862
  )
@@ -5060,12 +5870,12 @@ var InitializeService = class extends Context30.Tag("InitializeService")() {
5060
5870
  });
5061
5871
  yield* eventBus.on("sessionChanged", onSessionChanged);
5062
5872
  yield* eventBus.on("sessionProcessChanged", onSessionProcessChanged);
5063
- yield* Effect35.gen(function* () {
5873
+ yield* Effect36.gen(function* () {
5064
5874
  console.log("Initializing projects cache");
5065
5875
  const { projects } = yield* projectRepository.getProjects();
5066
5876
  console.log(`${projects.length} projects cache initialized`);
5067
5877
  console.log("Initializing sessions cache");
5068
- const results = yield* Effect35.all(
5878
+ const results = yield* Effect36.all(
5069
5879
  projects.map(
5070
5880
  (project) => sessionRepository.getSessions(project.id)
5071
5881
  ),
@@ -5077,12 +5887,12 @@ var InitializeService = class extends Context30.Tag("InitializeService")() {
5077
5887
  );
5078
5888
  console.log(`${totalSessions} sessions cache initialized`);
5079
5889
  }).pipe(
5080
- Effect35.catchAll(() => Effect35.void),
5081
- Effect35.withSpan("initialize-cache")
5890
+ Effect36.catchAll(() => Effect36.void),
5891
+ Effect36.withSpan("initialize-cache")
5082
5892
  );
5083
- }).pipe(Effect35.withSpan("start-initialization"));
5893
+ }).pipe(Effect36.withSpan("start-initialization"));
5084
5894
  };
5085
- const stopCleanup = () => Effect35.gen(function* () {
5895
+ const stopCleanup = () => Effect36.gen(function* () {
5086
5896
  const listeners = yield* Ref11.get(listenersRef);
5087
5897
  if (listeners.sessionChanged) {
5088
5898
  yield* eventBus.off("sessionChanged", listeners.sessionChanged);
@@ -5107,7 +5917,7 @@ var InitializeService = class extends Context30.Tag("InitializeService")() {
5107
5917
 
5108
5918
  // src/server/hono/route.ts
5109
5919
  import { zValidator } from "@hono/zod-validator";
5110
- import { Effect as Effect37, Runtime as Runtime3 } from "effect";
5920
+ import { Effect as Effect38, Runtime as Runtime3 } from "effect";
5111
5921
  import { setCookie as setCookie2 } from "hono/cookie";
5112
5922
  import { streamSSE } from "hono/streaming";
5113
5923
  import prexit from "prexit";
@@ -5116,7 +5926,7 @@ import { z as z28 } from "zod";
5116
5926
  // package.json
5117
5927
  var package_default = {
5118
5928
  name: "@kimuson/claude-code-viewer",
5119
- version: "0.4.4",
5929
+ version: "0.4.5",
5120
5930
  type: "module",
5121
5931
  license: "MIT",
5122
5932
  repository: {
@@ -5160,8 +5970,8 @@ var package_default = {
5160
5970
  "@anthropic-ai/claude-agent-sdk": "0.1.30",
5161
5971
  "@anthropic-ai/claude-code": "2.0.24",
5162
5972
  "@anthropic-ai/sdk": "0.67.0",
5163
- "@effect/platform": "0.92.1",
5164
- "@effect/platform-node": "0.98.4",
5973
+ "@effect/platform": "0.93.2",
5974
+ "@effect/platform-node": "0.100.0",
5165
5975
  "@hono/node-server": "1.19.5",
5166
5976
  "@hono/zod-validator": "0.7.4",
5167
5977
  "@lingui/core": "5.5.1",
@@ -5184,7 +5994,7 @@ var package_default = {
5184
5994
  "class-variance-authority": "0.7.1",
5185
5995
  clsx: "2.1.1",
5186
5996
  "date-fns": "4.1.0",
5187
- effect: "3.18.4",
5997
+ effect: "3.19.3",
5188
5998
  "es-toolkit": "1.41.0",
5189
5999
  hono: "4.10.3",
5190
6000
  jotai: "2.15.0",
@@ -5378,11 +6188,12 @@ var userConfigSchema = z27.object({
5378
6188
  locale: localeSchema.optional().default("en"),
5379
6189
  theme: z27.enum(["light", "dark", "system"]).optional().default("system")
5380
6190
  });
6191
+ var defaultUserConfig = userConfigSchema.parse({});
5381
6192
 
5382
6193
  // src/server/lib/effect/toEffectResponse.ts
5383
- import { Effect as Effect36 } from "effect";
6194
+ import { Effect as Effect37 } from "effect";
5384
6195
  var effectToResponse = async (ctx, effect) => {
5385
- const result = await Effect36.runPromise(effect);
6196
+ const result = await Effect37.runPromise(effect);
5386
6197
  const result2 = ctx.json(result.response, result.status);
5387
6198
  return result2;
5388
6199
  };
@@ -5414,12 +6225,8 @@ var configMiddleware = createMiddleware(
5414
6225
  c,
5415
6226
  "ccv-config",
5416
6227
  JSON.stringify({
5417
- hideNoUserMessageSession: true,
5418
- unifySameTitleSession: true,
5419
- enterKeyBehavior: "shift-enter-send",
5420
- permissionMode: "default",
5421
- locale: preferredLocale,
5422
- theme: "system"
6228
+ ...defaultUserConfig,
6229
+ locale: preferredLocale
5423
6230
  })
5424
6231
  );
5425
6232
  }
@@ -5429,7 +6236,7 @@ var configMiddleware = createMiddleware(
5429
6236
  );
5430
6237
 
5431
6238
  // src/server/hono/route.ts
5432
- var routes = (app) => Effect37.gen(function* () {
6239
+ var routes = (app) => Effect38.gen(function* () {
5433
6240
  const projectController = yield* ProjectController;
5434
6241
  const sessionController = yield* SessionController;
5435
6242
  const gitController = yield* GitController;
@@ -5444,7 +6251,7 @@ var routes = (app) => Effect37.gen(function* () {
5444
6251
  const userConfigService = yield* UserConfigService;
5445
6252
  const claudeCodeLifeCycleService = yield* ClaudeCodeLifeCycleService;
5446
6253
  const initializeService = yield* InitializeService;
5447
- const runtime = yield* Effect37.runtime();
6254
+ const runtime = yield* Effect38.runtime();
5448
6255
  if ((yield* envService.getEnv("NEXT_PHASE")) !== "phase-production-build") {
5449
6256
  yield* initializeService.startInitialization();
5450
6257
  prexit(async () => {
@@ -5452,7 +6259,7 @@ var routes = (app) => Effect37.gen(function* () {
5452
6259
  });
5453
6260
  }
5454
6261
  return app.use(configMiddleware).use(async (c, next) => {
5455
- await Effect37.runPromise(
6262
+ await Effect38.runPromise(
5456
6263
  userConfigService.setUserConfig({
5457
6264
  ...c.get("userConfig")
5458
6265
  })
@@ -5487,7 +6294,7 @@ var routes = (app) => Effect37.gen(function* () {
5487
6294
  projectController.getProject({
5488
6295
  ...c.req.param(),
5489
6296
  ...c.req.valid("query")
5490
- }).pipe(Effect37.provide(runtime))
6297
+ }).pipe(Effect38.provide(runtime))
5491
6298
  );
5492
6299
  return response;
5493
6300
  }
@@ -5504,7 +6311,7 @@ var routes = (app) => Effect37.gen(function* () {
5504
6311
  c,
5505
6312
  projectController.createProject({
5506
6313
  ...c.req.valid("json")
5507
- }).pipe(Effect37.provide(runtime))
6314
+ }).pipe(Effect38.provide(runtime))
5508
6315
  );
5509
6316
  return response;
5510
6317
  }
@@ -5513,21 +6320,30 @@ var routes = (app) => Effect37.gen(function* () {
5513
6320
  c,
5514
6321
  projectController.getProjectLatestSession({
5515
6322
  ...c.req.param()
5516
- }).pipe(Effect37.provide(runtime))
6323
+ }).pipe(Effect38.provide(runtime))
5517
6324
  );
5518
6325
  return response;
5519
6326
  }).get("/api/projects/:projectId/sessions/:sessionId", async (c) => {
5520
6327
  const response = await effectToResponse(
5521
6328
  c,
5522
- sessionController.getSession({ ...c.req.param() }).pipe(Effect37.provide(runtime))
6329
+ sessionController.getSession({ ...c.req.param() }).pipe(Effect38.provide(runtime))
5523
6330
  );
5524
6331
  return response;
5525
- }).get("/api/projects/:projectId/git/current-revisions", async (c) => {
6332
+ }).get(
6333
+ "/api/projects/:projectId/sessions/:sessionId/export",
6334
+ async (c) => {
6335
+ const response = await effectToResponse(
6336
+ c,
6337
+ sessionController.exportSessionHtml({ ...c.req.param() }).pipe(Effect38.provide(runtime))
6338
+ );
6339
+ return response;
6340
+ }
6341
+ ).get("/api/projects/:projectId/git/current-revisions", async (c) => {
5526
6342
  const response = await effectToResponse(
5527
6343
  c,
5528
6344
  gitController.getCurrentRevisions({
5529
6345
  ...c.req.param()
5530
- }).pipe(Effect37.provide(runtime))
6346
+ }).pipe(Effect38.provide(runtime))
5531
6347
  );
5532
6348
  return response;
5533
6349
  }).post(
@@ -5545,7 +6361,7 @@ var routes = (app) => Effect37.gen(function* () {
5545
6361
  gitController.getGitDiff({
5546
6362
  ...c.req.param(),
5547
6363
  ...c.req.valid("json")
5548
- }).pipe(Effect37.provide(runtime))
6364
+ }).pipe(Effect38.provide(runtime))
5549
6365
  );
5550
6366
  return response;
5551
6367
  }
@@ -5558,7 +6374,7 @@ var routes = (app) => Effect37.gen(function* () {
5558
6374
  gitController.commitFiles({
5559
6375
  ...c.req.param(),
5560
6376
  ...c.req.valid("json")
5561
- }).pipe(Effect37.provide(runtime))
6377
+ }).pipe(Effect38.provide(runtime))
5562
6378
  );
5563
6379
  return response;
5564
6380
  }
@@ -5571,7 +6387,7 @@ var routes = (app) => Effect37.gen(function* () {
5571
6387
  gitController.pushCommits({
5572
6388
  ...c.req.param(),
5573
6389
  ...c.req.valid("json")
5574
- }).pipe(Effect37.provide(runtime))
6390
+ }).pipe(Effect38.provide(runtime))
5575
6391
  );
5576
6392
  return response;
5577
6393
  }
@@ -5584,7 +6400,7 @@ var routes = (app) => Effect37.gen(function* () {
5584
6400
  gitController.commitAndPush({
5585
6401
  ...c.req.param(),
5586
6402
  ...c.req.valid("json")
5587
- }).pipe(Effect37.provide(runtime))
6403
+ }).pipe(Effect38.provide(runtime))
5588
6404
  );
5589
6405
  return response;
5590
6406
  }
@@ -5601,19 +6417,19 @@ var routes = (app) => Effect37.gen(function* () {
5601
6417
  c,
5602
6418
  claudeCodeController.getMcpListRoute({
5603
6419
  ...c.req.param()
5604
- }).pipe(Effect37.provide(runtime))
6420
+ }).pipe(Effect38.provide(runtime))
5605
6421
  );
5606
6422
  return response;
5607
6423
  }).get("/api/cc/meta", async (c) => {
5608
6424
  const response = await effectToResponse(
5609
6425
  c,
5610
- claudeCodeController.getClaudeCodeMeta().pipe(Effect37.provide(runtime))
6426
+ claudeCodeController.getClaudeCodeMeta().pipe(Effect38.provide(runtime))
5611
6427
  );
5612
6428
  return response;
5613
6429
  }).get("/api/cc/features", async (c) => {
5614
6430
  const response = await effectToResponse(
5615
6431
  c,
5616
- claudeCodeController.getAvailableFeatures().pipe(Effect37.provide(runtime))
6432
+ claudeCodeController.getAvailableFeatures().pipe(Effect38.provide(runtime))
5617
6433
  );
5618
6434
  return response;
5619
6435
  }).get("/api/cc/session-processes", async (c) => {
@@ -5657,7 +6473,7 @@ var routes = (app) => Effect37.gen(function* () {
5657
6473
  claudeCodeSessionProcessController.continueSessionProcess({
5658
6474
  ...c.req.param(),
5659
6475
  ...c.req.valid("json")
5660
- }).pipe(Effect37.provide(runtime))
6476
+ }).pipe(Effect38.provide(runtime))
5661
6477
  );
5662
6478
  return response;
5663
6479
  }
@@ -5666,7 +6482,7 @@ var routes = (app) => Effect37.gen(function* () {
5666
6482
  zValidator("json", z28.object({ projectId: z28.string() })),
5667
6483
  async (c) => {
5668
6484
  const { sessionProcessId } = c.req.param();
5669
- void Effect37.runFork(
6485
+ void Effect38.runFork(
5670
6486
  claudeCodeLifeCycleService.abortTask(sessionProcessId)
5671
6487
  );
5672
6488
  return c.json({ message: "Task aborted" });
@@ -5694,7 +6510,7 @@ var routes = (app) => Effect37.gen(function* () {
5694
6510
  c,
5695
6511
  async (rawStream) => {
5696
6512
  await Runtime3.runPromise(runtime)(
5697
- sseController.handleSSE(rawStream).pipe(Effect37.provide(TypeSafeSSE.make(rawStream)))
6513
+ sseController.handleSSE(rawStream).pipe(Effect38.provide(TypeSafeSSE.make(rawStream)))
5698
6514
  );
5699
6515
  },
5700
6516
  async (err) => {
@@ -5704,7 +6520,7 @@ var routes = (app) => Effect37.gen(function* () {
5704
6520
  }).get("/api/scheduler/jobs", async (c) => {
5705
6521
  const response = await effectToResponse(
5706
6522
  c,
5707
- schedulerController.getJobs().pipe(Effect37.provide(runtime))
6523
+ schedulerController.getJobs().pipe(Effect38.provide(runtime))
5708
6524
  );
5709
6525
  return response;
5710
6526
  }).post(
@@ -5715,7 +6531,7 @@ var routes = (app) => Effect37.gen(function* () {
5715
6531
  c,
5716
6532
  schedulerController.addJob({
5717
6533
  job: c.req.valid("json")
5718
- }).pipe(Effect37.provide(runtime))
6534
+ }).pipe(Effect38.provide(runtime))
5719
6535
  );
5720
6536
  return response;
5721
6537
  }
@@ -5728,7 +6544,7 @@ var routes = (app) => Effect37.gen(function* () {
5728
6544
  schedulerController.updateJob({
5729
6545
  id: c.req.param("id"),
5730
6546
  job: c.req.valid("json")
5731
- }).pipe(Effect37.provide(runtime))
6547
+ }).pipe(Effect38.provide(runtime))
5732
6548
  );
5733
6549
  return response;
5734
6550
  }
@@ -5737,7 +6553,7 @@ var routes = (app) => Effect37.gen(function* () {
5737
6553
  c,
5738
6554
  schedulerController.deleteJob({
5739
6555
  id: c.req.param("id")
5740
- }).pipe(Effect37.provide(runtime))
6556
+ }).pipe(Effect38.provide(runtime))
5741
6557
  );
5742
6558
  return response;
5743
6559
  }).get(
@@ -5779,7 +6595,7 @@ var routes = (app) => Effect37.gen(function* () {
5779
6595
  ).get("/api/flags", async (c) => {
5780
6596
  const response = await effectToResponse(
5781
6597
  c,
5782
- featureFlagController.getFlags().pipe(Effect37.provide(runtime))
6598
+ featureFlagController.getFlags().pipe(Effect38.provide(runtime))
5783
6599
  );
5784
6600
  return response;
5785
6601
  });
@@ -5816,42 +6632,42 @@ if (!isDevelopment) {
5816
6632
  }
5817
6633
  var program = routes(honoApp).pipe(
5818
6634
  /** Presentation */
5819
- Effect38.provide(ProjectController.Live),
5820
- Effect38.provide(SessionController.Live),
5821
- Effect38.provide(GitController.Live),
5822
- Effect38.provide(ClaudeCodeController.Live),
5823
- Effect38.provide(ClaudeCodeSessionProcessController.Live),
5824
- Effect38.provide(ClaudeCodePermissionController.Live),
5825
- Effect38.provide(FileSystemController.Live),
5826
- Effect38.provide(SSEController.Live),
5827
- Effect38.provide(SchedulerController.Live),
5828
- Effect38.provide(FeatureFlagController.Live)
6635
+ Effect39.provide(ProjectController.Live),
6636
+ Effect39.provide(SessionController.Live),
6637
+ Effect39.provide(GitController.Live),
6638
+ Effect39.provide(ClaudeCodeController.Live),
6639
+ Effect39.provide(ClaudeCodeSessionProcessController.Live),
6640
+ Effect39.provide(ClaudeCodePermissionController.Live),
6641
+ Effect39.provide(FileSystemController.Live),
6642
+ Effect39.provide(SSEController.Live),
6643
+ Effect39.provide(SchedulerController.Live),
6644
+ Effect39.provide(FeatureFlagController.Live)
5829
6645
  ).pipe(
5830
6646
  /** Application */
5831
- Effect38.provide(InitializeService.Live),
5832
- Effect38.provide(FileWatcherService.Live)
6647
+ Effect39.provide(InitializeService.Live),
6648
+ Effect39.provide(FileWatcherService.Live)
5833
6649
  ).pipe(
5834
6650
  /** Domain */
5835
- Effect38.provide(ClaudeCodeLifeCycleService.Live),
5836
- Effect38.provide(ClaudeCodePermissionService.Live),
5837
- Effect38.provide(ClaudeCodeSessionProcessService.Live),
5838
- Effect38.provide(ClaudeCodeService.Live),
5839
- Effect38.provide(GitService.Live),
5840
- Effect38.provide(SchedulerService.Live),
5841
- Effect38.provide(SchedulerConfigBaseDir.Live)
6651
+ Effect39.provide(ClaudeCodeLifeCycleService.Live),
6652
+ Effect39.provide(ClaudeCodePermissionService.Live),
6653
+ Effect39.provide(ClaudeCodeSessionProcessService.Live),
6654
+ Effect39.provide(ClaudeCodeService.Live),
6655
+ Effect39.provide(GitService.Live),
6656
+ Effect39.provide(SchedulerService.Live),
6657
+ Effect39.provide(SchedulerConfigBaseDir.Live)
5842
6658
  ).pipe(
5843
6659
  /** Infrastructure */
5844
- Effect38.provide(ProjectRepository.Live),
5845
- Effect38.provide(SessionRepository.Live),
5846
- Effect38.provide(ProjectMetaService.Live),
5847
- Effect38.provide(SessionMetaService.Live),
5848
- Effect38.provide(VirtualConversationDatabase.Live)
6660
+ Effect39.provide(ProjectRepository.Live),
6661
+ Effect39.provide(SessionRepository.Live),
6662
+ Effect39.provide(ProjectMetaService.Live),
6663
+ Effect39.provide(SessionMetaService.Live),
6664
+ Effect39.provide(VirtualConversationDatabase.Live)
5849
6665
  ).pipe(
5850
6666
  /** Platform */
5851
- Effect38.provide(platformLayer),
5852
- Effect38.provide(NodeContext2.layer)
6667
+ Effect39.provide(platformLayer),
6668
+ Effect39.provide(NodeContext2.layer)
5853
6669
  );
5854
- await Effect38.runPromise(program);
6670
+ await Effect39.runPromise(program);
5855
6671
  var port = isDevelopment ? (
5856
6672
  // biome-ignore lint/style/noProcessEnv: allow only here
5857
6673
  process.env.DEV_BE_PORT ?? "3401"