@oro.ad/nuxt-claude-devtools 1.2.0 → 1.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.
Files changed (115) hide show
  1. package/README.md +105 -13
  2. package/dist/client/200.html +1 -1
  3. package/dist/client/404.html +1 -1
  4. package/dist/client/_nuxt/B8uzckkK.js +1 -0
  5. package/dist/client/_nuxt/{CPQSDiF7.js → BAb1fJOF.js} +4 -4
  6. package/dist/client/_nuxt/BB1-kxmm.js +1 -0
  7. package/dist/client/_nuxt/BMZIbUUD.js +1 -0
  8. package/dist/client/_nuxt/BSF2Vz9o.js +1 -0
  9. package/dist/client/_nuxt/BSVkH7b6.js +1 -0
  10. package/dist/client/_nuxt/{08Mb3FOO.js → BYp73eMl.js} +1 -1
  11. package/dist/client/_nuxt/B_BoWmnX.js +1 -0
  12. package/dist/client/_nuxt/BcZxFXBD.js +1 -0
  13. package/dist/client/_nuxt/BflmC3YB.js +1 -0
  14. package/dist/client/_nuxt/BnXQTjo-.js +1 -0
  15. package/dist/client/_nuxt/C--9REmc.js +1 -0
  16. package/dist/client/_nuxt/CDQtmRaX.js +1 -0
  17. package/dist/client/_nuxt/CHeJJZL9.js +1 -0
  18. package/dist/client/_nuxt/{CSlPuO5s.js → COus5Ssl.js} +1 -1
  19. package/dist/client/_nuxt/{o1jjB-UO.js → CPA0s6N9.js} +1 -1
  20. package/dist/client/_nuxt/CRkq21kc.js +1 -0
  21. package/dist/client/_nuxt/Cgba93Y9.js +1 -0
  22. package/dist/client/_nuxt/D2l4TRxW.js +1 -0
  23. package/dist/client/_nuxt/DC_XB519.js +1 -0
  24. package/dist/client/_nuxt/DEys9N1G.js +1 -0
  25. package/dist/client/_nuxt/{b4Upel01.js → DGQ4s7ae.js} +1 -1
  26. package/dist/client/_nuxt/DH8Ugy8E.js +1 -0
  27. package/dist/client/_nuxt/DSt96JPY.js +4 -0
  28. package/dist/client/_nuxt/DbJLoP3G.js +1 -0
  29. package/dist/client/_nuxt/DeGmaFBY.js +1 -0
  30. package/dist/client/_nuxt/DgfRwrFR.js +1 -0
  31. package/dist/client/_nuxt/{CLKqRoht.js → DolUcBed.js} +1 -1
  32. package/dist/client/_nuxt/M6QPYocW.js +1 -0
  33. package/dist/client/_nuxt/QumocfwJ.js +1 -0
  34. package/dist/client/_nuxt/TQi6eIO6.js +1 -0
  35. package/dist/client/_nuxt/V4UvAcd3.js +1 -0
  36. package/dist/client/_nuxt/builds/latest.json +1 -1
  37. package/dist/client/_nuxt/builds/meta/2be12f06-336a-4fdd-b982-2f6c682c14a6.json +1 -0
  38. package/dist/client/_nuxt/d8BPa19J.js +1 -0
  39. package/dist/client/_nuxt/entry.BMxUr06A.css +1 -0
  40. package/dist/client/_nuxt/qbS8UemQ.js +1 -0
  41. package/dist/client/_nuxt/wDw60tEC.js +10 -0
  42. package/dist/client/_nuxt/xEjB6ozD.js +1 -0
  43. package/dist/client/agents/index.html +1 -1
  44. package/dist/client/commands/index.html +1 -1
  45. package/dist/client/docs/index.html +1 -1
  46. package/dist/client/index.html +1 -1
  47. package/dist/client/mcp/index.html +1 -1
  48. package/dist/client/plugins/index.html +1 -0
  49. package/dist/client/settings/index.html +1 -0
  50. package/dist/client/skills/index.html +1 -1
  51. package/dist/module.d.mts +12 -1
  52. package/dist/module.json +1 -1
  53. package/dist/module.mjs +27 -5
  54. package/dist/runtime/constants.d.ts +29 -0
  55. package/dist/runtime/constants.js +5 -0
  56. package/dist/runtime/overlay/components/ChatOverlay.d.vue.ts +7 -0
  57. package/dist/runtime/overlay/components/ChatOverlay.vue +893 -0
  58. package/dist/runtime/overlay/components/ChatOverlay.vue.d.ts +7 -0
  59. package/dist/runtime/overlay/components/MarkdownContent.d.vue.ts +6 -0
  60. package/dist/runtime/overlay/components/MarkdownContent.vue +31 -0
  61. package/dist/runtime/overlay/components/MarkdownContent.vue.d.ts +6 -0
  62. package/dist/runtime/overlay/components/ToolCallBlock.d.vue.ts +8 -0
  63. package/dist/runtime/overlay/components/ToolCallBlock.vue +77 -0
  64. package/dist/runtime/overlay/components/ToolCallBlock.vue.d.ts +8 -0
  65. package/dist/runtime/overlay/plugin.client.d.ts +6 -0
  66. package/dist/runtime/overlay/plugin.client.js +29 -0
  67. package/dist/runtime/server/agents-manager.d.ts +17 -4
  68. package/dist/runtime/server/agents-manager.js +38 -109
  69. package/dist/runtime/server/base-resource-manager.d.ts +90 -0
  70. package/dist/runtime/server/base-resource-manager.js +201 -0
  71. package/dist/runtime/server/claude-session.d.ts +11 -1
  72. package/dist/runtime/server/claude-session.js +246 -27
  73. package/dist/runtime/server/commands-manager.d.ts +12 -4
  74. package/dist/runtime/server/commands-manager.js +25 -100
  75. package/dist/runtime/server/constants.d.ts +94 -0
  76. package/dist/runtime/server/constants.js +18 -0
  77. package/dist/runtime/server/docs-manager.d.ts +7 -0
  78. package/dist/runtime/server/docs-manager.js +112 -3
  79. package/dist/runtime/server/history-manager.d.ts +1 -0
  80. package/dist/runtime/server/history-manager.js +25 -3
  81. package/dist/runtime/server/plugins/socket.io.js +5 -3
  82. package/dist/runtime/server/plugins-manager.d.ts +84 -0
  83. package/dist/runtime/server/plugins-manager.js +338 -0
  84. package/dist/runtime/server/settings-manager.d.ts +17 -0
  85. package/dist/runtime/server/settings-manager.js +70 -0
  86. package/dist/runtime/server/share-manager.d.ts +24 -0
  87. package/dist/runtime/server/share-manager.js +96 -0
  88. package/dist/runtime/server/skills-manager.d.ts +18 -4
  89. package/dist/runtime/server/skills-manager.js +32 -159
  90. package/dist/runtime/shared/composables/useClaudeChat.d.ts +35 -0
  91. package/dist/runtime/shared/composables/useClaudeChat.js +264 -0
  92. package/dist/runtime/shared/composables/useShare.d.ts +42 -0
  93. package/dist/runtime/shared/composables/useShare.js +192 -0
  94. package/dist/runtime/shared/composables/useVoiceInput.d.ts +8 -0
  95. package/dist/runtime/shared/composables/useVoiceInput.js +77 -0
  96. package/dist/runtime/shared/constants.d.ts +28 -0
  97. package/dist/runtime/shared/constants.js +6 -0
  98. package/dist/runtime/shared/index.d.ts +9 -0
  99. package/dist/runtime/shared/index.js +5 -0
  100. package/dist/runtime/shared/types.d.ts +48 -0
  101. package/dist/runtime/shared/types.js +0 -0
  102. package/dist/runtime/types.d.ts +2 -0
  103. package/dist/types.d.mts +1 -1
  104. package/package.json +5 -3
  105. package/dist/client/_nuxt/BMi2eT6G.js +0 -1
  106. package/dist/client/_nuxt/BiBLVxWh.js +0 -1
  107. package/dist/client/_nuxt/BnRGpZsC.js +0 -8
  108. package/dist/client/_nuxt/C2GhPw5d.js +0 -7
  109. package/dist/client/_nuxt/D8683igF.js +0 -7
  110. package/dist/client/_nuxt/DBIw6BGF.js +0 -1
  111. package/dist/client/_nuxt/DCgjfr8H.js +0 -9
  112. package/dist/client/_nuxt/bl5iU4Kz.js +0 -1
  113. package/dist/client/_nuxt/builds/meta/35284331-5e85-46e0-9058-988fea05336c.json +0 -1
  114. package/dist/client/_nuxt/entry.BhHeZ_Nj.css +0 -1
  115. package/dist/client/_nuxt/nKfsBgPE.js +0 -4
@@ -1,134 +1,28 @@
1
- import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
1
  import { createLogger } from "../logger.js";
2
+ import { MarkdownResourceManager } from "./base-resource-manager.js";
3
+ import { SKILL_FILE, SKILLS_SUBDIR } from "./constants.js";
4
4
  const log = createLogger("skills", { timestamp: true });
5
- export class SkillsManager {
6
- skillsDir;
5
+ export class SkillsManager extends MarkdownResourceManager {
7
6
  constructor(projectPath) {
8
- this.skillsDir = join(projectPath, ".claude", "skills");
9
- if (!existsSync(this.skillsDir)) {
10
- mkdirSync(this.skillsDir, { recursive: true });
11
- log("Created skills directory", { path: this.skillsDir });
12
- }
13
- }
14
- // Parse YAML frontmatter from markdown content
15
- parseFrontmatter(content) {
16
- const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
17
- const match = content.match(frontmatterRegex);
18
- if (!match) {
19
- return { frontmatter: {}, body: content.trim() };
20
- }
21
- const [, yaml, body] = match;
22
- const frontmatter = {};
23
- for (const line of yaml.split("\n")) {
24
- const colonIndex = line.indexOf(":");
25
- if (colonIndex === -1) continue;
26
- const key = line.slice(0, colonIndex).trim();
27
- const value = line.slice(colonIndex + 1).trim();
28
- switch (key) {
29
- case "name":
30
- frontmatter.name = value;
31
- break;
32
- case "description":
33
- frontmatter.description = value;
34
- break;
35
- case "argument-hint":
36
- frontmatter["argument-hint"] = value;
37
- break;
38
- case "disable-model-invocation":
39
- frontmatter["disable-model-invocation"] = value === "true";
40
- break;
41
- case "user-invocable":
42
- frontmatter["user-invocable"] = value === "true";
43
- break;
44
- case "allowed-tools":
45
- frontmatter["allowed-tools"] = value;
46
- break;
47
- case "model":
48
- frontmatter.model = value;
49
- break;
50
- case "context":
51
- frontmatter.context = value;
52
- break;
53
- case "agent":
54
- frontmatter.agent = value;
55
- break;
56
- }
57
- }
58
- return { frontmatter, body: body.trim() };
7
+ super(projectPath, SKILLS_SUBDIR, log, {
8
+ useSubdirectories: true,
9
+ subdirectoryFilename: SKILL_FILE
10
+ });
59
11
  }
60
- // Build frontmatter string
61
12
  buildFrontmatter(skill) {
62
- const lines = ["---"];
63
- if (skill.name) {
64
- lines.push(`name: ${skill.name}`);
65
- }
66
- if (skill.description) {
67
- lines.push(`description: ${skill.description}`);
68
- }
69
- if (skill.argumentHint) {
70
- lines.push(`argument-hint: ${skill.argumentHint}`);
71
- }
72
- if (skill.disableModelInvocation !== void 0) {
73
- lines.push(`disable-model-invocation: ${skill.disableModelInvocation}`);
74
- }
75
- if (skill.userInvocable !== void 0) {
76
- lines.push(`user-invocable: ${skill.userInvocable}`);
77
- }
78
- if (skill.allowedTools && skill.allowedTools.length > 0) {
79
- lines.push(`allowed-tools: ${skill.allowedTools.join(", ")}`);
80
- }
81
- if (skill.model) {
82
- lines.push(`model: ${skill.model}`);
83
- }
84
- if (skill.context) {
85
- lines.push(`context: ${skill.context}`);
86
- }
87
- if (skill.agent) {
88
- lines.push(`agent: ${skill.agent}`);
89
- }
90
- lines.push("---");
91
- return lines.join("\n");
13
+ return this.buildFrontmatterLines([
14
+ { key: "name", value: skill.name },
15
+ { key: "description", value: skill.description },
16
+ { key: "argument-hint", value: skill.argumentHint },
17
+ { key: "disable-model-invocation", value: skill.disableModelInvocation },
18
+ { key: "user-invocable", value: skill.userInvocable },
19
+ { key: "allowed-tools", value: skill.allowedTools },
20
+ { key: "model", value: skill.model },
21
+ { key: "context", value: skill.context },
22
+ { key: "agent", value: skill.agent }
23
+ ]);
92
24
  }
93
- // Get all skills
94
- getSkills() {
95
- const skills = [];
96
- if (!existsSync(this.skillsDir)) return skills;
97
- const entries = readdirSync(this.skillsDir);
98
- for (const entry of entries) {
99
- const skillDir = join(this.skillsDir, entry);
100
- const stat = statSync(skillDir);
101
- if (!stat.isDirectory()) continue;
102
- const skillFile = join(skillDir, "SKILL.md");
103
- if (!existsSync(skillFile)) continue;
104
- const fileStat = statSync(skillFile);
105
- const rawContent = readFileSync(skillFile, "utf-8");
106
- const { frontmatter, body } = this.parseFrontmatter(rawContent);
107
- skills.push({
108
- name: frontmatter.name || entry,
109
- description: frontmatter.description || "",
110
- content: body,
111
- rawContent,
112
- argumentHint: frontmatter["argument-hint"],
113
- disableModelInvocation: frontmatter["disable-model-invocation"],
114
- userInvocable: frontmatter["user-invocable"],
115
- allowedTools: frontmatter["allowed-tools"] ? frontmatter["allowed-tools"].split(",").map((s) => s.trim()) : void 0,
116
- model: frontmatter.model,
117
- context: frontmatter.context === "fork" ? "fork" : void 0,
118
- agent: frontmatter.agent,
119
- updatedAt: fileStat.mtime.toISOString()
120
- });
121
- }
122
- return skills.sort((a, b) => a.name.localeCompare(b.name));
123
- }
124
- // Get single skill
125
- getSkill(name) {
126
- const skillDir = join(this.skillsDir, name);
127
- const skillFile = join(skillDir, "SKILL.md");
128
- if (!existsSync(skillFile)) return null;
129
- const stat = statSync(skillFile);
130
- const rawContent = readFileSync(skillFile, "utf-8");
131
- const { frontmatter, body } = this.parseFrontmatter(rawContent);
25
+ toResource(name, frontmatter, body, rawContent, updatedAt) {
132
26
  return {
133
27
  name: frontmatter.name || name,
134
28
  description: frontmatter.description || "",
@@ -141,33 +35,21 @@ export class SkillsManager {
141
35
  model: frontmatter.model,
142
36
  context: frontmatter.context === "fork" ? "fork" : void 0,
143
37
  agent: frontmatter.agent,
144
- updatedAt: stat.mtime.toISOString()
38
+ updatedAt
145
39
  };
146
40
  }
147
- // Get skill names only (for agent skills selector)
41
+ // Public API methods
42
+ getSkills() {
43
+ return this.getAll();
44
+ }
45
+ getSkill(name) {
46
+ return this.getOne(name);
47
+ }
148
48
  getSkillNames() {
149
- if (!existsSync(this.skillsDir)) return [];
150
- const names = [];
151
- const entries = readdirSync(this.skillsDir);
152
- for (const entry of entries) {
153
- const skillDir = join(this.skillsDir, entry);
154
- const stat = statSync(skillDir);
155
- if (!stat.isDirectory()) continue;
156
- const skillFile = join(skillDir, "SKILL.md");
157
- if (existsSync(skillFile)) {
158
- names.push(entry);
159
- }
160
- }
161
- return names.sort();
49
+ return this.getAll().map((s) => s.name).sort();
162
50
  }
163
- // Create or update skill
164
51
  saveSkill(skill) {
165
- const safeName = skill.name.replace(/[^\w-]/g, "-").toLowerCase();
166
- const skillDir = join(this.skillsDir, safeName);
167
- const skillFile = join(skillDir, "SKILL.md");
168
- if (!existsSync(skillDir)) {
169
- mkdirSync(skillDir, { recursive: true });
170
- }
52
+ const safeName = this.sanitizeName(skill.name);
171
53
  const frontmatter = this.buildFrontmatter({
172
54
  name: safeName,
173
55
  description: skill.description,
@@ -179,11 +61,7 @@ export class SkillsManager {
179
61
  context: skill.context,
180
62
  agent: skill.agent
181
63
  });
182
- const rawContent = `${frontmatter}
183
-
184
- ${skill.content}`;
185
- writeFileSync(skillFile, rawContent, "utf-8");
186
- log("Saved skill", { name: safeName, path: skillFile });
64
+ const { rawContent, updatedAt } = this.saveResource(safeName, frontmatter, skill.content);
187
65
  return {
188
66
  name: safeName,
189
67
  description: skill.description,
@@ -196,15 +74,10 @@ ${skill.content}`;
196
74
  model: skill.model,
197
75
  context: skill.context,
198
76
  agent: skill.agent,
199
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
77
+ updatedAt
200
78
  };
201
79
  }
202
- // Delete skill
203
80
  deleteSkill(name) {
204
- const skillDir = join(this.skillsDir, name);
205
- if (!existsSync(skillDir)) return false;
206
- rmSync(skillDir, { recursive: true });
207
- log("Deleted skill", { name });
208
- return true;
81
+ return this.delete(name);
209
82
  }
210
83
  }
@@ -0,0 +1,35 @@
1
+ import type { ComputedRef, Ref } from 'vue';
2
+ import type { Socket } from 'socket.io-client';
3
+ import type { ContentBlock, Conversation, DocFile, Message, SlashCommand } from '../types.js';
4
+ interface UseChatOptions {
5
+ onDocsReceived?: (docs: DocFile[]) => void;
6
+ onCommandsReceived?: (commands: SlashCommand[]) => void;
7
+ log?: (...args: unknown[]) => void;
8
+ /** Custom socket URL (for tunnel support) */
9
+ socketUrl?: string;
10
+ /** Current user ID for collaborative mode */
11
+ getCurrentUserId?: () => string | null;
12
+ }
13
+ /** Return type for useClaudeChat composable */
14
+ export interface UseChatReturn {
15
+ socket: Ref<Socket | null>;
16
+ messages: Ref<Message[]>;
17
+ conversations: Ref<Conversation[]>;
18
+ activeConversationId: Ref<string | null>;
19
+ isConnected: Ref<boolean>;
20
+ isSessionActive: Ref<boolean>;
21
+ isProcessing: Ref<boolean>;
22
+ isHistoryOpen: Ref<boolean>;
23
+ statusText: ComputedRef<string>;
24
+ statusColor: ComputedRef<string>;
25
+ connectSocket: () => void;
26
+ disconnect: () => void;
27
+ newChat: () => void;
28
+ sendMessage: (content: string, senderId?: string, senderNickname?: string) => void;
29
+ addMessage: (role: Message['role'], content: string, streaming?: boolean) => Message;
30
+ toggleHistory: () => void;
31
+ selectConversation: (id: string) => void;
32
+ findToolResult: (blocks: ContentBlock[] | undefined, toolUseId: string) => ContentBlock | undefined;
33
+ }
34
+ export declare function useClaudeChat(options?: UseChatOptions): UseChatReturn;
35
+ export {};
@@ -0,0 +1,264 @@
1
+ import { computed, ref } from "vue";
2
+ import { io } from "socket.io-client";
3
+ import { SOCKET_PATH } from "../constants.js";
4
+ export function useClaudeChat(options = {}) {
5
+ const { log = () => {
6
+ }, onDocsReceived, onCommandsReceived, socketUrl, getCurrentUserId } = options;
7
+ const socket = ref(null);
8
+ const messages = ref([]);
9
+ const conversations = ref([]);
10
+ const activeConversationId = ref(null);
11
+ const isConnected = ref(false);
12
+ const isSessionActive = ref(false);
13
+ const isProcessing = ref(false);
14
+ const isHistoryOpen = ref(false);
15
+ const pendingToolCalls = ref(/* @__PURE__ */ new Map());
16
+ const statusText = computed(() => {
17
+ if (!isConnected.value) return "Disconnected";
18
+ if (isProcessing.value) return "Processing...";
19
+ return "Ready";
20
+ });
21
+ const statusColor = computed(() => {
22
+ if (!isConnected.value) return "red";
23
+ if (isProcessing.value) return "blue";
24
+ return "green";
25
+ });
26
+ function generateId() {
27
+ return Math.random().toString(36).substring(2, 9);
28
+ }
29
+ function addMessage(role, content, streaming = false) {
30
+ const message = {
31
+ id: generateId(),
32
+ role,
33
+ content,
34
+ timestamp: /* @__PURE__ */ new Date(),
35
+ streaming
36
+ };
37
+ messages.value.push(message);
38
+ return message;
39
+ }
40
+ function getSocketUrl() {
41
+ if (socketUrl) return socketUrl;
42
+ if (typeof window !== "undefined") {
43
+ return window.location.origin;
44
+ }
45
+ return "";
46
+ }
47
+ function connectSocket() {
48
+ const url = getSocketUrl();
49
+ log("Connecting to socket at", url);
50
+ socket.value = io(url, {
51
+ path: SOCKET_PATH,
52
+ transports: ["websocket", "polling"],
53
+ reconnection: true,
54
+ reconnectionAttempts: 5
55
+ });
56
+ socket.value.on("connect", () => {
57
+ log("Connected to socket");
58
+ isConnected.value = true;
59
+ socket.value?.emit("docs:list");
60
+ socket.value?.emit("commands:list");
61
+ });
62
+ socket.value.on("disconnect", () => {
63
+ log("Disconnected from socket");
64
+ isConnected.value = false;
65
+ isSessionActive.value = false;
66
+ isProcessing.value = false;
67
+ });
68
+ socket.value.on("session:status", (status) => {
69
+ log("Session status:", status);
70
+ isSessionActive.value = status.active;
71
+ isProcessing.value = status.processing;
72
+ });
73
+ socket.value.on("history:loaded", (conversation) => {
74
+ log("History loaded:", conversation.id);
75
+ activeConversationId.value = conversation.id;
76
+ messages.value = conversation.messages.map((m) => ({
77
+ ...m,
78
+ timestamp: new Date(m.timestamp)
79
+ }));
80
+ });
81
+ socket.value.on("history:list", (convs) => {
82
+ log("Conversations list:", convs.length);
83
+ conversations.value = convs;
84
+ });
85
+ socket.value.on("history:switched", (conversation) => {
86
+ log("Switched to conversation:", conversation.id);
87
+ activeConversationId.value = conversation.id;
88
+ messages.value = conversation.messages.map((m) => ({
89
+ ...m,
90
+ timestamp: new Date(m.timestamp)
91
+ }));
92
+ isHistoryOpen.value = false;
93
+ });
94
+ socket.value.on("docs:list", (files) => {
95
+ log("Docs list received:", files.length);
96
+ onDocsReceived?.(files);
97
+ });
98
+ socket.value.on("commands:list", (cmds) => {
99
+ log("Commands list received:", cmds.length);
100
+ onCommandsReceived?.(cmds);
101
+ });
102
+ socket.value.on("stream:message_start", (data) => {
103
+ log("Message start:", data.id);
104
+ pendingToolCalls.value.clear();
105
+ const lastMessage = messages.value[messages.value.length - 1];
106
+ if (!lastMessage || lastMessage.role !== "assistant" || !lastMessage.streaming) {
107
+ addMessage("assistant", "", true);
108
+ }
109
+ const streamingMessage = messages.value.findLast((m) => m.role === "assistant" && m.streaming);
110
+ if (streamingMessage && !streamingMessage.contentBlocks) {
111
+ streamingMessage.contentBlocks = [];
112
+ }
113
+ });
114
+ socket.value.on("stream:tool_use", (data) => {
115
+ log("Tool use:", data.name);
116
+ const toolBlock = {
117
+ type: "tool_use",
118
+ id: data.id,
119
+ name: data.name,
120
+ input: data.input
121
+ };
122
+ pendingToolCalls.value.set(data.id, toolBlock);
123
+ const lastMessage = messages.value.findLast((m) => m.role === "assistant" && m.streaming) || messages.value.findLast((m) => m.role === "assistant");
124
+ if (lastMessage) {
125
+ if (!lastMessage.contentBlocks) {
126
+ lastMessage.contentBlocks = [];
127
+ }
128
+ lastMessage.contentBlocks.push(toolBlock);
129
+ }
130
+ });
131
+ socket.value.on("stream:tool_result", (data) => {
132
+ log("Tool result:", data.tool_use_id);
133
+ const resultBlock = {
134
+ type: "tool_result",
135
+ tool_use_id: data.tool_use_id,
136
+ content: data.content,
137
+ is_error: data.is_error
138
+ };
139
+ const lastMessage = messages.value.findLast((m) => m.role === "assistant" && m.streaming) || messages.value.findLast((m) => m.role === "assistant");
140
+ if (lastMessage) {
141
+ if (!lastMessage.contentBlocks) {
142
+ lastMessage.contentBlocks = [];
143
+ }
144
+ lastMessage.contentBlocks.push(resultBlock);
145
+ }
146
+ });
147
+ socket.value.on("stream:text_delta", (data) => {
148
+ const lastMessage = messages.value.findLast((m) => m.role === "assistant" && m.streaming);
149
+ if (lastMessage) {
150
+ lastMessage.content += data.text;
151
+ if (!lastMessage.contentBlocks) {
152
+ lastMessage.contentBlocks = [];
153
+ }
154
+ const lastBlock = lastMessage.contentBlocks[lastMessage.contentBlocks.length - 1];
155
+ if (lastBlock && lastBlock.type === "text") {
156
+ lastBlock.text = (lastBlock.text || "") + data.text;
157
+ } else {
158
+ const prefix = lastMessage.contentBlocks.length > 0 ? "\n" : "";
159
+ lastMessage.contentBlocks.push({
160
+ type: "text",
161
+ text: prefix + data.text
162
+ });
163
+ }
164
+ }
165
+ });
166
+ socket.value.on("stream:message_complete", (data) => {
167
+ log("Message complete:", data.id);
168
+ const lastMessage = messages.value.findLast((m) => m.role === "assistant" && m.streaming) || messages.value.findLast((m) => m.role === "assistant");
169
+ if (lastMessage) {
170
+ lastMessage.streaming = false;
171
+ lastMessage.content = data.content;
172
+ lastMessage.contentBlocks = data.contentBlocks;
173
+ lastMessage.model = data.model;
174
+ }
175
+ pendingToolCalls.value.clear();
176
+ });
177
+ socket.value.on("output:error", (error) => {
178
+ log("Output error:", error);
179
+ addMessage("system", `Error: ${error}`);
180
+ });
181
+ socket.value.on("session:error", (error) => {
182
+ log("Session error:", error);
183
+ addMessage("system", `Session error: ${error}`);
184
+ });
185
+ socket.value.on("stream:system_message", (data) => {
186
+ log("System message:", data.message);
187
+ addMessage("system", data.message);
188
+ });
189
+ socket.value.on("stream:user_message", (data) => {
190
+ const currentUserId = getCurrentUserId?.();
191
+ log("User message received:", { senderId: data.senderId, currentUserId, isOwn: data.senderId === currentUserId });
192
+ if (currentUserId && data.senderId === currentUserId) {
193
+ return;
194
+ }
195
+ messages.value.push({
196
+ ...data,
197
+ timestamp: new Date(data.timestamp)
198
+ });
199
+ addMessage("assistant", "", true);
200
+ });
201
+ }
202
+ function newChat() {
203
+ if (socket.value) {
204
+ socket.value.emit("session:reset");
205
+ messages.value = [];
206
+ isHistoryOpen.value = false;
207
+ }
208
+ }
209
+ function sendMessage(content, senderId, senderNickname) {
210
+ if (!content.trim() || isProcessing.value || !isConnected.value) return false;
211
+ const userMsg = addMessage("user", content);
212
+ if (senderId) {
213
+ userMsg.senderId = senderId;
214
+ userMsg.senderNickname = senderNickname;
215
+ }
216
+ addMessage("assistant", "", true);
217
+ if (socket.value) {
218
+ socket.value.emit("message:send", senderId ? { message: content, senderId } : content);
219
+ }
220
+ return true;
221
+ }
222
+ function toggleHistory() {
223
+ isHistoryOpen.value = !isHistoryOpen.value;
224
+ if (isHistoryOpen.value && socket.value) {
225
+ socket.value.emit("history:list");
226
+ }
227
+ }
228
+ function selectConversation(id) {
229
+ if (socket.value) {
230
+ socket.value.emit("history:switch", id);
231
+ }
232
+ }
233
+ function disconnect() {
234
+ if (socket.value) {
235
+ socket.value.disconnect();
236
+ }
237
+ }
238
+ function findToolResult(blocks, toolUseId) {
239
+ if (!blocks) return void 0;
240
+ return blocks.find((b) => b.type === "tool_result" && b.tool_use_id === toolUseId);
241
+ }
242
+ return {
243
+ // State
244
+ socket,
245
+ messages,
246
+ conversations,
247
+ activeConversationId,
248
+ isConnected,
249
+ isSessionActive,
250
+ isProcessing,
251
+ isHistoryOpen,
252
+ statusText,
253
+ statusColor,
254
+ // Methods
255
+ connectSocket,
256
+ disconnect,
257
+ newChat,
258
+ sendMessage,
259
+ addMessage,
260
+ toggleHistory,
261
+ selectConversation,
262
+ findToolResult
263
+ };
264
+ }
@@ -0,0 +1,42 @@
1
+ import type { Socket } from 'socket.io-client';
2
+ import type { ShareUser } from '../types.js';
3
+ interface ShareOptions {
4
+ /** Get tunnel URL for share links */
5
+ getTunnelUrl?: () => string | null;
6
+ /** Log function */
7
+ log?: (...args: unknown[]) => void;
8
+ }
9
+ export declare function useShare(options?: ShareOptions): {
10
+ userId: import("vue").Ref<string | null, string | null>;
11
+ nickname: import("vue").Ref<string | null, string | null>;
12
+ users: import("vue").Ref<{
13
+ id: string;
14
+ nickname: string;
15
+ joinedAt: string;
16
+ lastSeen: string;
17
+ }[], ShareUser[] | {
18
+ id: string;
19
+ nickname: string;
20
+ joinedAt: string;
21
+ lastSeen: string;
22
+ }[]>;
23
+ showNicknameModal: import("vue").Ref<boolean, boolean>;
24
+ nicknameError: import("vue").Ref<string | null, string | null>;
25
+ isShareMode: import("vue").ComputedRef<boolean>;
26
+ wasInvited: import("vue").Ref<boolean, boolean>;
27
+ sharingActiveOnServer: import("vue").Ref<boolean, boolean>;
28
+ nicknameFromUrl: import("vue").Ref<boolean, boolean>;
29
+ initShare: () => void;
30
+ createShareLink: (includeNickname?: boolean) => string;
31
+ setNickname: (name: string) => void;
32
+ needsNicknameImmediate: () => boolean;
33
+ needsNicknameForMessage: () => boolean;
34
+ needsNicknameForShare: () => boolean;
35
+ checkSharingStatus: (socket: Socket | null) => void;
36
+ registerUser: (socket: Socket | null) => void;
37
+ setupSocketListeners: (socket: Socket) => void;
38
+ getNicknameById: (id: string) => string | null;
39
+ isOwnMessage: (senderId?: string) => boolean;
40
+ copyShareLink: (includeNickname?: boolean) => Promise<boolean>;
41
+ };
42
+ export {};