@oro.ad/nuxt-claude-devtools 1.3.0 → 1.5.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 (111) hide show
  1. package/README.md +2 -1
  2. package/dist/client/200.html +1 -1
  3. package/dist/client/404.html +1 -1
  4. package/dist/client/_nuxt/{COus5Ssl.js → 0BAoaFXM.js} +1 -1
  5. package/dist/client/_nuxt/{B_BoWmnX.js → 88aFj9mk.js} +1 -1
  6. package/dist/client/_nuxt/{DolUcBed.js → B5vRr8Ti.js} +1 -1
  7. package/dist/client/_nuxt/{CDQtmRaX.js → BI4BFakr.js} +1 -1
  8. package/dist/client/_nuxt/{CPA0s6N9.js → BJuQJ8yP.js} +1 -1
  9. package/dist/client/_nuxt/{BB1-kxmm.js → BMxGt8ZG.js} +1 -1
  10. package/dist/client/_nuxt/{TQi6eIO6.js → Baq9Hzkz.js} +1 -1
  11. package/dist/client/_nuxt/{DbJLoP3G.js → Bf779K50.js} +1 -1
  12. package/dist/client/_nuxt/BmF8r-gh.js +1 -0
  13. package/dist/client/_nuxt/BryQ1L4F.js +1 -0
  14. package/dist/client/_nuxt/{V4UvAcd3.js → BsG0VKct.js} +1 -1
  15. package/dist/client/_nuxt/Bt-DdLhQ.js +12 -0
  16. package/dist/client/_nuxt/{CHeJJZL9.js → C6Xm_PzB.js} +1 -1
  17. package/dist/client/_nuxt/{DC_XB519.js → C8AkZ-W3.js} +1 -1
  18. package/dist/client/_nuxt/{BSVkH7b6.js → CEM1iRz_.js} +1 -1
  19. package/dist/client/_nuxt/{BcZxFXBD.js → CETmet01.js} +1 -1
  20. package/dist/client/_nuxt/CG1tBJBN.js +1 -0
  21. package/dist/client/_nuxt/{D2l4TRxW.js → CXOn8Upj.js} +1 -1
  22. package/dist/client/_nuxt/CYomf6PL.js +1 -0
  23. package/dist/client/_nuxt/{DEys9N1G.js → CrhTgxiU.js} +1 -1
  24. package/dist/client/_nuxt/{BYp73eMl.js → CtWfW1XP.js} +1 -1
  25. package/dist/client/_nuxt/{d8BPa19J.js → CuUOvg0u.js} +1 -1
  26. package/dist/client/_nuxt/CxYvL5O5.js +1 -0
  27. package/dist/client/_nuxt/D8DUrd91.js +1 -0
  28. package/dist/client/_nuxt/{DSt96JPY.js → DFkDlupw.js} +2 -2
  29. package/dist/client/_nuxt/{B8uzckkK.js → DG-9quB_.js} +1 -1
  30. package/dist/client/_nuxt/DHlhBFAg.js +1 -0
  31. package/dist/client/_nuxt/{BnXQTjo-.js → DJw5U4z8.js} +1 -1
  32. package/dist/client/_nuxt/{M6QPYocW.js → DRReoGGV.js} +1 -1
  33. package/dist/client/_nuxt/{DGQ4s7ae.js → DSMailoF.js} +1 -1
  34. package/dist/client/_nuxt/{qbS8UemQ.js → DUPgidXP.js} +1 -1
  35. package/dist/client/_nuxt/{QumocfwJ.js → D_JyZ6Hh.js} +1 -1
  36. package/dist/client/_nuxt/{BAb1fJOF.js → Dgm9zqhP.js} +2 -2
  37. package/dist/client/_nuxt/builds/latest.json +1 -1
  38. package/dist/client/_nuxt/builds/meta/024950b2-9b05-4789-9918-c859c91d6b13.json +1 -0
  39. package/dist/client/_nuxt/{C--9REmc.js → h1c4XpR7.js} +1 -1
  40. package/dist/client/_nuxt/kN0L7CSs.js +1 -0
  41. package/dist/client/_nuxt/{BMZIbUUD.js → n4TwJfzb.js} +1 -1
  42. package/dist/client/_nuxt/zY60w9UT.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 -1
  49. package/dist/client/settings/index.html +1 -1
  50. package/dist/client/skills/index.html +1 -1
  51. package/dist/module.json +1 -1
  52. package/dist/module.mjs +10 -4
  53. package/dist/runtime/overlay/components/ChatOverlay.d.vue.ts +0 -1
  54. package/dist/runtime/overlay/components/ChatOverlay.vue +221 -731
  55. package/dist/runtime/overlay/components/ChatOverlay.vue.d.ts +0 -1
  56. package/dist/runtime/overlay/components/MarkdownContent.vue +1 -1
  57. package/dist/runtime/overlay/components/ToolCallBlock.vue +1 -1
  58. package/dist/runtime/overlay/components/chat/ChatHeader.d.vue.ts +18 -0
  59. package/dist/runtime/overlay/components/chat/ChatHeader.vue +82 -0
  60. package/dist/runtime/overlay/components/chat/ChatHeader.vue.d.ts +18 -0
  61. package/dist/runtime/overlay/components/chat/ChatInput.d.vue.ts +22 -0
  62. package/dist/runtime/overlay/components/chat/ChatInput.vue +526 -0
  63. package/dist/runtime/overlay/components/chat/ChatInput.vue.d.ts +22 -0
  64. package/dist/runtime/overlay/components/chat/ChatMessages.d.vue.ts +13 -0
  65. package/dist/runtime/overlay/components/chat/ChatMessages.vue +160 -0
  66. package/dist/runtime/overlay/components/chat/ChatMessages.vue.d.ts +13 -0
  67. package/dist/runtime/overlay/components/chat/ClaudeBadge.d.vue.ts +22 -0
  68. package/dist/runtime/overlay/components/chat/ClaudeBadge.vue +85 -0
  69. package/dist/runtime/overlay/components/chat/ClaudeBadge.vue.d.ts +22 -0
  70. package/dist/runtime/overlay/components/chat/HistoryPanel.d.vue.ts +17 -0
  71. package/dist/runtime/overlay/components/chat/HistoryPanel.vue +65 -0
  72. package/dist/runtime/overlay/components/chat/HistoryPanel.vue.d.ts +17 -0
  73. package/dist/runtime/overlay/components/chat/NicknameModal.d.vue.ts +13 -0
  74. package/dist/runtime/overlay/components/chat/NicknameModal.vue +89 -0
  75. package/dist/runtime/overlay/components/chat/NicknameModal.vue.d.ts +13 -0
  76. package/dist/runtime/overlay/components/chat/index.d.ts +6 -0
  77. package/dist/runtime/overlay/components/chat/index.js +6 -0
  78. package/dist/runtime/overlay/composables/index.d.ts +3 -0
  79. package/dist/runtime/overlay/composables/index.js +3 -0
  80. package/dist/runtime/overlay/composables/useMessageContext.d.ts +25 -0
  81. package/dist/runtime/overlay/composables/useMessageContext.js +29 -0
  82. package/dist/runtime/overlay/composables/useMobileSwipe.d.ts +15 -0
  83. package/dist/runtime/overlay/composables/useMobileSwipe.js +79 -0
  84. package/dist/runtime/overlay/composables/usePanelInteraction.d.ts +30 -0
  85. package/dist/runtime/overlay/composables/usePanelInteraction.js +184 -0
  86. package/dist/runtime/overlay/composables/usePanelPosition.d.ts +69 -0
  87. package/dist/runtime/overlay/composables/usePanelPosition.js +147 -0
  88. package/dist/runtime/server/claude-session.d.ts +11 -0
  89. package/dist/runtime/server/claude-session.js +166 -6
  90. package/dist/runtime/shared/composables/useClaudeChat.d.ts +7 -3
  91. package/dist/runtime/shared/composables/useClaudeChat.js +27 -251
  92. package/dist/runtime/shared/composables/useClaudeChatCore.d.ts +40 -0
  93. package/dist/runtime/shared/composables/useClaudeChatCore.js +350 -0
  94. package/dist/runtime/shared/composables/useMessageContext.d.ts +54 -0
  95. package/dist/runtime/shared/composables/useMessageContext.js +195 -0
  96. package/dist/runtime/shared/composables/useShare.d.ts +14 -4
  97. package/dist/runtime/shared/composables/useShare.js +57 -35
  98. package/dist/runtime/shared/composables/useVoiceInput.js +40 -11
  99. package/dist/runtime/shared/types.d.ts +36 -0
  100. package/dist/runtime/types.d.ts +14 -0
  101. package/package.json +1 -1
  102. package/dist/client/_nuxt/BSF2Vz9o.js +0 -1
  103. package/dist/client/_nuxt/BflmC3YB.js +0 -1
  104. package/dist/client/_nuxt/CRkq21kc.js +0 -1
  105. package/dist/client/_nuxt/Cgba93Y9.js +0 -1
  106. package/dist/client/_nuxt/DH8Ugy8E.js +0 -1
  107. package/dist/client/_nuxt/DeGmaFBY.js +0 -1
  108. package/dist/client/_nuxt/DgfRwrFR.js +0 -1
  109. package/dist/client/_nuxt/builds/meta/2be12f06-336a-4fdd-b982-2f6c682c14a6.json +0 -1
  110. package/dist/client/_nuxt/wDw60tEC.js +0 -10
  111. package/dist/client/_nuxt/xEjB6ozD.js +0 -1
@@ -0,0 +1,160 @@
1
+ <script setup>
2
+ import { nextTick, ref, watch } from "vue";
3
+ import MarkdownContent from "../MarkdownContent.vue";
4
+ import ToolCallBlock from "../ToolCallBlock.vue";
5
+ const props = defineProps({
6
+ messages: { type: Array, required: true },
7
+ isShareMode: { type: Boolean, required: true },
8
+ findToolResult: { type: Function, required: true },
9
+ isOwnMessage: { type: Function, required: true }
10
+ });
11
+ const messagesContainer = ref(null);
12
+ function stripContextBlock(content) {
13
+ return content.replace(/^\[context\]\n[\s\S]*?\n\[\/context\]\n?/, "").trim();
14
+ }
15
+ function getDisplayContent(message) {
16
+ return stripContextBlock(message.content);
17
+ }
18
+ function getMessageClass(role) {
19
+ if (role === "user") return "claude-message-user";
20
+ if (role === "assistant") return "claude-message-assistant";
21
+ return "claude-message-system";
22
+ }
23
+ function scrollToBottom() {
24
+ if (messagesContainer.value) {
25
+ messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
26
+ }
27
+ }
28
+ watch(
29
+ () => props.messages.length,
30
+ () => nextTick(scrollToBottom)
31
+ );
32
+ watch(
33
+ () => props.messages[props.messages.length - 1]?.content,
34
+ () => nextTick(scrollToBottom)
35
+ );
36
+ defineExpose({ scrollToBottom });
37
+ </script>
38
+
39
+ <template>
40
+ <div
41
+ ref="messagesContainer"
42
+ class="claude-messages"
43
+ >
44
+ <div
45
+ v-if="messages.length === 0"
46
+ class="claude-empty"
47
+ >
48
+ <p>
49
+ Start a conversation with Claude
50
+ </p>
51
+ <p class="claude-empty-hint">
52
+ Type a message or use /commands
53
+ </p>
54
+ </div>
55
+
56
+ <div
57
+ v-for="message in messages"
58
+ :key="message.id"
59
+ :class="[
60
+ 'claude-message',
61
+ getMessageClass(message.role),
62
+ {
63
+ 'claude-message-own': message.role === 'user' && isOwnMessage(message.senderId),
64
+ 'claude-message-other': message.role === 'user' && !isOwnMessage(message.senderId)
65
+ }
66
+ ]"
67
+ >
68
+ <!-- User message -->
69
+ <template v-if="message.role === 'user'">
70
+ <div
71
+ v-if="isShareMode && message.senderNickname"
72
+ class="claude-message-sender"
73
+ >
74
+ {{ message.senderNickname }}
75
+ <span
76
+ v-if="isOwnMessage(message.senderId)"
77
+ class="claude-message-you"
78
+ >(you)</span>
79
+ </div>
80
+ <!-- File attachments -->
81
+ <div
82
+ v-if="message.attachments?.length"
83
+ class="claude-message-attachments"
84
+ >
85
+ <div
86
+ v-for="attachment in message.attachments"
87
+ :key="attachment.path"
88
+ class="claude-attachment-badge"
89
+ :title="attachment.filename"
90
+ >
91
+ <!-- Image icon -->
92
+ <svg
93
+ v-if="attachment.mimeType?.startsWith('image/')"
94
+ fill="currentColor"
95
+ height="14"
96
+ viewBox="0 0 24 24"
97
+ width="14"
98
+ >
99
+ <path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z" />
100
+ </svg>
101
+ <!-- File icon -->
102
+ <svg
103
+ v-else
104
+ fill="currentColor"
105
+ height="14"
106
+ viewBox="0 0 24 24"
107
+ width="14"
108
+ >
109
+ <path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z" />
110
+ </svg>
111
+ <span>{{ attachment.filename }}</span>
112
+ </div>
113
+ </div>
114
+ <div class="claude-message-content">
115
+ {{ getDisplayContent(message) }}
116
+ </div>
117
+ </template>
118
+
119
+ <!-- Assistant message -->
120
+ <template v-else-if="message.role === 'assistant'">
121
+ <div class="claude-message-content">
122
+ <template v-if="message.contentBlocks?.length">
123
+ <template
124
+ v-for="(block, idx) in message.contentBlocks"
125
+ :key="idx"
126
+ >
127
+ <MarkdownContent
128
+ v-if="block.type === 'text' && block.text"
129
+ :content="stripContextBlock(block.text)"
130
+ />
131
+ <ToolCallBlock
132
+ v-else-if="block.type === 'tool_use'"
133
+ :block="block"
134
+ :result="findToolResult(message.contentBlocks, block.id)"
135
+ />
136
+ </template>
137
+ </template>
138
+ <template v-else-if="message.content">
139
+ <MarkdownContent :content="getDisplayContent(message)" />
140
+ </template>
141
+ <span
142
+ v-if="message.streaming"
143
+ class="claude-cursor"
144
+ />
145
+ </div>
146
+ </template>
147
+
148
+ <!-- System message -->
149
+ <template v-else>
150
+ <div class="claude-message-content claude-message-system-content">
151
+ {{ getDisplayContent(message) }}
152
+ </div>
153
+ </template>
154
+ </div>
155
+ </div>
156
+ </template>
157
+
158
+ <style>
159
+ .claude-messages{gap:16px;overflow-y:auto;padding:80px 20px 140px;position:relative;z-index:1}.claude-empty,.claude-messages{display:flex;flex:1;flex-direction:column}.claude-empty{align-items:center;color:var(--claude-text-muted);justify-content:center;margin-top:-60px;padding:0 20px;text-align:center}.claude-empty p:first-child{background:linear-gradient(135deg,var(--claude-text) 0,var(--claude-text-muted) 100%);-webkit-background-clip:text;font-size:16px;font-weight:500;margin-bottom:8px;-webkit-text-fill-color:transparent;background-clip:text}.claude-empty-hint{font-size:13px;margin-top:4px;opacity:.6}.claude-message{animation:claude-message-in .3s ease-out;max-width:88%}@keyframes claude-message-in{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.claude-message-user{align-self:flex-end}.claude-message-sender{color:var(--claude-primary);font-size:11px;font-weight:600;margin-bottom:6px;padding-left:4px;text-shadow:0 0 20px var(--claude-primary-glow)}.claude-message-you{font-weight:400;opacity:.5}.claude-message-own{align-self:flex-end}.claude-message-own .claude-message-content{backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);background:var(--claude-user-bg);border:1px solid rgba(254,154,0,.2);border-radius:var(--claude-radius-sm) var(--claude-radius-sm) 4px var(--claude-radius-sm);box-shadow:0 4px 15px rgba(254,154,0,.15);padding:12px 16px;white-space:pre-wrap;word-break:break-word}.claude-message-other{align-self:flex-start}.claude-message-other .claude-message-content{background:var(--claude-other-user-bg);border:1px solid rgba(236,72,153,.2);border-radius:var(--claude-radius-sm) var(--claude-radius-sm) var(--claude-radius-sm) 4px;box-shadow:0 4px 15px rgba(236,72,153,.1)}.claude-message-other .claude-message-content,.claude-message-user .claude-message-content{backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);padding:12px 16px;white-space:pre-wrap;word-break:break-word}.claude-message-user .claude-message-content{background:var(--claude-user-bg);border:1px solid rgba(254,154,0,.2);border-radius:var(--claude-radius-sm) var(--claude-radius-sm) 4px var(--claude-radius-sm)}.claude-message-attachments{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}.claude-attachment-badge{align-items:center;background:rgba(254,154,0,.15);border:1px solid rgba(254,154,0,.3);border-radius:8px;color:var(--claude-primary);display:flex;font-size:12px;gap:6px;max-width:180px;padding:6px 10px}.claude-attachment-badge svg{flex-shrink:0;opacity:.8}.claude-attachment-badge span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.claude-message-assistant{align-self:flex-start}.claude-message-assistant .claude-message-content{backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);background:var(--claude-assistant-bg);border:1px solid var(--claude-border);border-radius:4px var(--claude-radius-sm) var(--claude-radius-sm) var(--claude-radius-sm);padding:12px 16px}.claude-message-system{align-self:center}.claude-message-system-content{background:var(--claude-system-bg);border:1px solid var(--claude-border);border-radius:20px;color:var(--claude-text-muted);font-size:12px;padding:8px 16px}.claude-cursor{animation:claude-cursor-blink 1s ease-in-out infinite;background:linear-gradient(180deg,var(--claude-primary) 0,#fbbf24 100%);border-radius:2px;box-shadow:0 0 10px var(--claude-primary-glow);display:inline-block;height:18px;margin-left:4px;width:3px}@keyframes claude-cursor-blink{0%,to{opacity:1}50%{opacity:.3}}.claude-messages::-webkit-scrollbar{width:6px}.claude-messages::-webkit-scrollbar-track{background:transparent}.claude-messages::-webkit-scrollbar-thumb{background:var(--claude-border);border-radius:3px}.claude-messages::-webkit-scrollbar-thumb:hover{background:var(--claude-text-dim)}@media (max-width:640px){.claude-messages{padding:90px 16px 160px}}
160
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { ContentBlock, Message } from '../../../shared/types.js';
2
+ type __VLS_Props = {
3
+ messages: Message[];
4
+ isShareMode: boolean;
5
+ findToolResult: (blocks: ContentBlock[] | undefined, toolUseId: string) => ContentBlock | undefined;
6
+ isOwnMessage: (senderId?: string) => boolean;
7
+ };
8
+ declare function scrollToBottom(): void;
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
10
+ scrollToBottom: typeof scrollToBottom;
11
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
+ declare const _default: typeof __VLS_export;
13
+ export default _default;
@@ -0,0 +1,22 @@
1
+ type __VLS_Props = {
2
+ statusColor: 'green' | 'blue' | 'red';
3
+ position: {
4
+ x: number;
5
+ y: number;
6
+ };
7
+ };
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
+ click: () => any;
10
+ dragEnd: (position: {
11
+ x: number;
12
+ y: number;
13
+ }) => any;
14
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
15
+ onClick?: (() => any) | undefined;
16
+ onDragEnd?: ((position: {
17
+ x: number;
18
+ y: number;
19
+ }) => any) | undefined;
20
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
+ declare const _default: typeof __VLS_export;
22
+ export default _default;
@@ -0,0 +1,85 @@
1
+ <script setup>
2
+ import { ref, watch } from "vue";
3
+ const props = defineProps({
4
+ statusColor: { type: String, required: true },
5
+ position: { type: Object, required: true }
6
+ });
7
+ const emit = defineEmits(["click", "dragEnd"]);
8
+ const badgeRef = ref(null);
9
+ const isDragging = ref(false);
10
+ const dragStart = ref({ x: 0, y: 0, posX: 0, posY: 0 });
11
+ const dragMoved = ref(false);
12
+ const currentPos = ref({ x: props.position.x, y: props.position.y });
13
+ function handleDragStart(e) {
14
+ const clientX = "touches" in e ? e.touches[0]?.clientX ?? 0 : e.clientX;
15
+ const clientY = "touches" in e ? e.touches[0]?.clientY ?? 0 : e.clientY;
16
+ dragStart.value = {
17
+ x: clientX,
18
+ y: clientY,
19
+ posX: currentPos.value.x,
20
+ posY: currentPos.value.y
21
+ };
22
+ isDragging.value = true;
23
+ dragMoved.value = false;
24
+ window.addEventListener("mousemove", handleDragMove);
25
+ window.addEventListener("mouseup", handleDragEnd);
26
+ window.addEventListener("touchmove", handleDragMove, { passive: false });
27
+ window.addEventListener("touchend", handleDragEnd);
28
+ }
29
+ function handleDragMove(e) {
30
+ if (!isDragging.value) return;
31
+ e.preventDefault();
32
+ const clientX = "touches" in e ? e.touches[0]?.clientX ?? 0 : e.clientX;
33
+ const clientY = "touches" in e ? e.touches[0]?.clientY ?? 0 : e.clientY;
34
+ const deltaX = dragStart.value.x - clientX;
35
+ const deltaY = dragStart.value.y - clientY;
36
+ if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
37
+ dragMoved.value = true;
38
+ }
39
+ const vw = window.innerWidth;
40
+ const vh = window.innerHeight;
41
+ const padding = 16;
42
+ let newX = dragStart.value.posX + deltaX;
43
+ let newY = dragStart.value.posY + deltaY;
44
+ newX = Math.max(padding, Math.min(vw - 120, newX));
45
+ newY = Math.max(padding, Math.min(vh - 60, newY));
46
+ currentPos.value = { x: newX, y: newY };
47
+ }
48
+ function handleDragEnd() {
49
+ isDragging.value = false;
50
+ window.removeEventListener("mousemove", handleDragMove);
51
+ window.removeEventListener("mouseup", handleDragEnd);
52
+ window.removeEventListener("touchmove", handleDragMove);
53
+ window.removeEventListener("touchend", handleDragEnd);
54
+ if (dragMoved.value) {
55
+ emit("dragEnd", currentPos.value);
56
+ }
57
+ }
58
+ function handleClick() {
59
+ if (!dragMoved.value) {
60
+ emit("click");
61
+ }
62
+ }
63
+ watch(() => props.position, (newPos) => {
64
+ currentPos.value = { x: newPos.x, y: newPos.y };
65
+ }, { immediate: true, deep: true });
66
+ </script>
67
+
68
+ <template>
69
+ <button
70
+ ref="badgeRef"
71
+ :class="['claude-badge', { 'claude-badge-dragging': isDragging }]"
72
+ :style="{ right: `${currentPos.x}px`, bottom: `${currentPos.y}px` }"
73
+ title="Chat with Claude (Ctrl+Shift+K) — drag to move"
74
+ @click="handleClick"
75
+ @mousedown="handleDragStart"
76
+ @touchstart="handleDragStart"
77
+ >
78
+ <span class="claude-badge-title">Claude</span>
79
+ <span :class="['claude-badge-status', `claude-badge-status-${statusColor}`]" />
80
+ </button>
81
+ </template>
82
+
83
+ <style>
84
+ .claude-badge{align-items:center;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);background:rgba(0,0,0,.6);border:1px solid hsla(0,0%,100%,.15);border-radius:var(--claude-radius-sm);box-shadow:0 8px 32px rgba(0,0,0,.4),0 0 0 1px rgba(254,154,0,.1);color:hsla(0,0%,100%,.95);cursor:pointer;display:flex;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;gap:10px;padding:10px 16px;position:fixed;transition:all .3s cubic-bezier(.4,0,.2,1);z-index:99998}.claude-badge:hover{background:rgba(0,0,0,.7);border-color:rgba(254,154,0,.3);box-shadow:0 12px 40px rgba(0,0,0,.5),0 0 30px rgba(254,154,0,.15);transform:translateY(-2px)}.claude-badge-dragging{box-shadow:0 16px 50px rgba(0,0,0,.6),0 0 40px rgba(254,154,0,.2);cursor:grabbing;transform:scale(1.05);transition:none}.claude-badge-title{font-size:14px;font-weight:600;letter-spacing:.01em}.claude-badge-status{border-radius:50%;flex-shrink:0;height:8px;width:8px}.claude-badge-status-green{background:#4ade80;box-shadow:0 0 8px rgba(74,222,128,.6)}.claude-badge-status-blue{animation:claude-badge-pulse 1.5s ease-in-out infinite;background:#fbbf24;box-shadow:0 0 8px rgba(251,191,36,.6)}.claude-badge-status-red{background:#f87171;box-shadow:0 0 8px hsla(0,91%,71%,.6)}@keyframes claude-badge-pulse{0%,to{opacity:1}50%{opacity:.5}}@media (max-width:640px){.claude-badge{padding:8px 14px}.claude-badge-title{font-size:13px}}
85
+ </style>
@@ -0,0 +1,22 @@
1
+ type __VLS_Props = {
2
+ statusColor: 'green' | 'blue' | 'red';
3
+ position: {
4
+ x: number;
5
+ y: number;
6
+ };
7
+ };
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
+ click: () => any;
10
+ dragEnd: (position: {
11
+ x: number;
12
+ y: number;
13
+ }) => any;
14
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
15
+ onClick?: (() => any) | undefined;
16
+ onDragEnd?: ((position: {
17
+ x: number;
18
+ y: number;
19
+ }) => any) | undefined;
20
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
+ declare const _default: typeof __VLS_export;
22
+ export default _default;
@@ -0,0 +1,17 @@
1
+ type __VLS_Props = {
2
+ show: boolean;
3
+ conversations: Array<{
4
+ id: string;
5
+ title?: string;
6
+ updatedAt: string;
7
+ }>;
8
+ };
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ close: () => any;
11
+ select: (id: string) => any;
12
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
13
+ onClose?: (() => any) | undefined;
14
+ onSelect?: ((id: string) => any) | undefined;
15
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
@@ -0,0 +1,65 @@
1
+ <script setup>
2
+ defineProps({
3
+ show: { type: Boolean, required: true },
4
+ conversations: { type: Array, required: true }
5
+ });
6
+ const emit = defineEmits(["close", "select"]);
7
+ function stripContextBlock(content) {
8
+ return content.replace(/^\[context\]\n[\s\S]*?\n\[\/context\]\n?/, "").trim();
9
+ }
10
+ function formatDate(dateStr) {
11
+ const date = new Date(dateStr);
12
+ return date.toLocaleDateString() + " " + date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
13
+ }
14
+ </script>
15
+
16
+ <template>
17
+ <Transition name="claude-slide-right">
18
+ <div
19
+ v-if="show"
20
+ class="claude-history"
21
+ >
22
+ <div class="claude-history-header">
23
+ <span>History</span>
24
+ <button
25
+ class="claude-btn-icon"
26
+ @click="emit('close')"
27
+ >
28
+ <svg
29
+ fill="currentColor"
30
+ height="16"
31
+ viewBox="0 0 24 24"
32
+ width="16"
33
+ >
34
+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
35
+ </svg>
36
+ </button>
37
+ </div>
38
+ <div class="claude-history-list">
39
+ <div
40
+ v-for="conv in conversations"
41
+ :key="conv.id"
42
+ class="claude-history-item"
43
+ @click="emit('select', conv.id)"
44
+ >
45
+ <div class="claude-history-title">
46
+ {{ stripContextBlock(conv.title || "") || "Untitled" }}
47
+ </div>
48
+ <div class="claude-history-date">
49
+ {{ formatDate(conv.updatedAt) }}
50
+ </div>
51
+ </div>
52
+ <div
53
+ v-if="conversations.length === 0"
54
+ class="claude-history-empty"
55
+ >
56
+ No conversations yet
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </Transition>
61
+ </template>
62
+
63
+ <style>
64
+ .claude-history{backdrop-filter:blur(var(--claude-blur-heavy));-webkit-backdrop-filter:blur(var(--claude-blur-heavy));background:rgba(0,0,0,.6);bottom:0;display:flex;flex-direction:column;left:0;position:absolute;right:0;top:0;z-index:10}.claude-history-header{align-items:center;background:transparent;display:flex;justify-content:space-between;padding:16px 20px;position:relative;z-index:1}.claude-history-header>span{align-items:center;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);background:rgba(0,0,0,.4);border:1px solid hsla(0,0%,100%,.1);border-radius:var(--claude-radius-sm);box-shadow:0 4px 12px rgba(0,0,0,.3);display:flex;font-size:15px;font-weight:600;gap:10px;padding:8px 14px}.claude-history-list{flex:1;margin-top:-60px;overflow-y:auto;padding:70px 12px 12px}.claude-history-item{background:hsla(0,0%,100%,.03);border:1px solid hsla(0,0%,100%,.05);border-radius:var(--claude-radius-sm);cursor:pointer;margin-bottom:8px;padding:14px 16px;transition:all .2s ease}.claude-history-item:hover{background:rgba(254,154,0,.1);border-color:rgba(254,154,0,.2);transform:translateX(4px)}.claude-history-title{color:var(--claude-text);font-weight:500;margin-bottom:6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.claude-history-date{color:var(--claude-text-dim);font-size:12px}.claude-history-empty{color:var(--claude-text-muted);font-size:14px;padding:48px 24px;text-align:center}.claude-slide-right-enter-active,.claude-slide-right-leave-active{transition:transform .3s cubic-bezier(.4,0,.2,1)}.claude-slide-right-enter-from,.claude-slide-right-leave-to{transform:translateX(100%)}
65
+ </style>
@@ -0,0 +1,17 @@
1
+ type __VLS_Props = {
2
+ show: boolean;
3
+ conversations: Array<{
4
+ id: string;
5
+ title?: string;
6
+ updatedAt: string;
7
+ }>;
8
+ };
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ close: () => any;
11
+ select: (id: string) => any;
12
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
13
+ onClose?: (() => any) | undefined;
14
+ onSelect?: ((id: string) => any) | undefined;
15
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
@@ -0,0 +1,13 @@
1
+ type __VLS_Props = {
2
+ show: boolean;
3
+ error?: string;
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
+ cancel: () => any;
7
+ submit: (nickname: string) => any;
8
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
9
+ onCancel?: (() => any) | undefined;
10
+ onSubmit?: ((nickname: string) => any) | undefined;
11
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
+ declare const _default: typeof __VLS_export;
13
+ export default _default;
@@ -0,0 +1,89 @@
1
+ <script setup>
2
+ import { ref } from "vue";
3
+ defineProps({
4
+ show: { type: Boolean, required: true },
5
+ error: { type: String, required: false }
6
+ });
7
+ const emit = defineEmits(["submit", "cancel"]);
8
+ const nicknameInput = ref("");
9
+ function handleSubmit() {
10
+ const name = nicknameInput.value.trim();
11
+ if (name.length >= 2 && name.length <= 20) {
12
+ emit("submit", name);
13
+ nicknameInput.value = "";
14
+ }
15
+ }
16
+ function handleCancel() {
17
+ emit("cancel");
18
+ nicknameInput.value = "";
19
+ }
20
+ </script>
21
+
22
+ <template>
23
+ <Teleport to="body">
24
+ <Transition name="claude-modal-fade">
25
+ <div
26
+ v-if="show"
27
+ class="claude-modal-backdrop"
28
+ @click.self="handleCancel"
29
+ >
30
+ <div class="claude-modal">
31
+ <div class="claude-modal-header">
32
+ <h3>Enter your nickname</h3>
33
+ <button
34
+ class="claude-btn-icon"
35
+ @click="handleCancel"
36
+ >
37
+ <svg
38
+ fill="currentColor"
39
+ height="16"
40
+ viewBox="0 0 24 24"
41
+ width="16"
42
+ >
43
+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
44
+ </svg>
45
+ </button>
46
+ </div>
47
+ <div class="claude-modal-body">
48
+ <p class="claude-modal-hint">
49
+ Choose a nickname for collaborative chat sessions
50
+ </p>
51
+ <input
52
+ v-model="nicknameInput"
53
+ class="claude-modal-input"
54
+ maxlength="20"
55
+ placeholder="Your nickname (2-20 characters)"
56
+ type="text"
57
+ @keydown.enter="handleSubmit"
58
+ >
59
+ <p
60
+ v-if="error"
61
+ class="claude-modal-error"
62
+ >
63
+ {{ error }}
64
+ </p>
65
+ </div>
66
+ <div class="claude-modal-actions">
67
+ <button
68
+ class="claude-modal-btn-secondary"
69
+ @click="handleCancel"
70
+ >
71
+ Cancel
72
+ </button>
73
+ <button
74
+ :disabled="nicknameInput.trim().length < 2"
75
+ class="claude-modal-btn-primary"
76
+ @click="handleSubmit"
77
+ >
78
+ Save
79
+ </button>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ </Transition>
84
+ </Teleport>
85
+ </template>
86
+
87
+ <style>
88
+ .claude-modal-backdrop{align-items:center;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);background:rgba(0,0,0,.5);display:flex;inset:0;justify-content:center;padding:24px;position:fixed;z-index:100000}.claude-modal{backdrop-filter:blur(var(--claude-blur-heavy));-webkit-backdrop-filter:blur(var(--claude-blur-heavy));background:var(--claude-glass-solid);border:1px solid var(--claude-border-light);border-radius:var(--claude-radius);box-shadow:var(--claude-shadow-lg),var(--claude-glow);max-width:380px;overflow:hidden;width:100%}.claude-modal-header{align-items:center;display:flex;justify-content:space-between;padding:20px 20px 16px}.claude-modal-header h3{background:linear-gradient(135deg,#fff,hsla(0,0%,100%,.8));-webkit-background-clip:text;font-size:17px;font-weight:600;margin:0;-webkit-text-fill-color:transparent;background-clip:text}.claude-modal-body{padding:20px}.claude-modal-hint{color:var(--claude-text-muted);font-size:14px;line-height:1.5;margin:0 0 16px}.claude-modal-input{background:var(--claude-glass);border:1px solid var(--claude-border);border-radius:var(--claude-radius-sm);color:var(--claude-text);font-family:inherit;font-size:14px;padding:12px 16px;transition:all .2s ease;width:100%}.claude-modal-input:focus{background:var(--claude-glass-elevated);border-color:var(--claude-primary);box-shadow:0 0 0 3px rgba(254,154,0,.15);outline:none}.claude-modal-input::-moz-placeholder{color:var(--claude-text-dim)}.claude-modal-input::placeholder{color:var(--claude-text-dim)}.claude-modal-error{color:#f87171;font-size:13px;margin:10px 0 0}.claude-modal-actions{display:flex;gap:10px;justify-content:flex-end;padding:16px 20px 20px}.claude-modal-btn-primary,.claude-modal-btn-secondary{border:none;border-radius:var(--claude-radius-sm);cursor:pointer;font-size:14px;font-weight:500;padding:10px 20px;transition:all .2s ease}.claude-modal-btn-secondary{background:var(--claude-glass);border:1px solid var(--claude-border);color:var(--claude-text)}.claude-modal-btn-secondary:hover{background:var(--claude-glass-hover);border-color:var(--claude-border-light)}.claude-modal-btn-primary{background:linear-gradient(135deg,#fe9a00,#f59e0b);box-shadow:0 4px 15px rgba(254,154,0,.3);color:#fff}.claude-modal-btn-primary:hover:not(:disabled){box-shadow:0 6px 20px rgba(254,154,0,.4);transform:translateY(-1px)}.claude-modal-btn-primary:disabled{box-shadow:none;cursor:not-allowed;opacity:.4;transform:none}.claude-modal-fade-enter-active,.claude-modal-fade-leave-active{transition:opacity .3s ease}.claude-modal-fade-enter-from,.claude-modal-fade-leave-to{opacity:0}
89
+ </style>
@@ -0,0 +1,13 @@
1
+ type __VLS_Props = {
2
+ show: boolean;
3
+ error?: string;
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
+ cancel: () => any;
7
+ submit: (nickname: string) => any;
8
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
9
+ onCancel?: (() => any) | undefined;
10
+ onSubmit?: ((nickname: string) => any) | undefined;
11
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
+ declare const _default: typeof __VLS_export;
13
+ export default _default;
@@ -0,0 +1,6 @@
1
+ export { default as ChatHeader } from './ChatHeader.vue.js';
2
+ export { default as ChatInput } from './ChatInput.vue.js';
3
+ export { default as ChatMessages } from './ChatMessages.vue.js';
4
+ export { default as ClaudeBadge } from './ClaudeBadge.vue.js';
5
+ export { default as HistoryPanel } from './HistoryPanel.vue.js';
6
+ export { default as NicknameModal } from './NicknameModal.vue.js';
@@ -0,0 +1,6 @@
1
+ export { default as ChatHeader } from "./ChatHeader.vue";
2
+ export { default as ChatInput } from "./ChatInput.vue";
3
+ export { default as ChatMessages } from "./ChatMessages.vue";
4
+ export { default as ClaudeBadge } from "./ClaudeBadge.vue";
5
+ export { default as HistoryPanel } from "./HistoryPanel.vue";
6
+ export { default as NicknameModal } from "./NicknameModal.vue";
@@ -0,0 +1,3 @@
1
+ export { usePanelPosition, type PanelAnchor, type PanelPositionOptions } from './usePanelPosition.js';
2
+ export { usePanelInteraction, type PanelInteractionOptions } from './usePanelInteraction.js';
3
+ export { useMobileSwipe, type MobileSwipeOptions } from './useMobileSwipe.js';
@@ -0,0 +1,3 @@
1
+ export { usePanelPosition } from "./usePanelPosition.js";
2
+ export { usePanelInteraction } from "./usePanelInteraction.js";
3
+ export { useMobileSwipe } from "./useMobileSwipe.js";
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Simple overlay version - uses window directly
3
+ */
4
+ export declare function useMessageContext(): {
5
+ contextChips: import("@vue/reactivity").Ref<{
6
+ id: "viewport" | "user-agent" | "routing";
7
+ label: string;
8
+ icon: string;
9
+ active: boolean;
10
+ }[], import("./useMessageContext.js").ContextChip[] | {
11
+ id: "viewport" | "user-agent" | "routing";
12
+ label: string;
13
+ icon: string;
14
+ active: boolean;
15
+ }[]>;
16
+ toggleContextChip: (id: import("./useMessageContext.js").ContextChip["id"]) => void;
17
+ collectContext: () => import("./useMessageContext.js").MessageContextData | null;
18
+ generateContextBlock: (context: import("./useMessageContext.js").MessageContextData) => string;
19
+ parseMessageContext: (content: string) => {
20
+ context: import("./useMessageContext.js").MessageContextData | null;
21
+ body: string;
22
+ };
23
+ collectContextBlock: () => string | null;
24
+ };
25
+ export type { ContextChip, MessageContextData, RouteInfo } from '../../shared/composables/useMessageContext.js';
@@ -0,0 +1,29 @@
1
+ import { useMessageContext as useMessageContextCore } from "../../shared/composables/useMessageContext.js";
2
+ export function useMessageContext() {
3
+ function getViewport() {
4
+ if (typeof window === "undefined") return null;
5
+ return {
6
+ width: window.innerWidth,
7
+ height: window.innerHeight
8
+ };
9
+ }
10
+ function getRoute() {
11
+ if (typeof window === "undefined") return null;
12
+ const searchParams = new URLSearchParams(window.location.search);
13
+ const query = {};
14
+ searchParams.forEach((value, key) => {
15
+ query[key] = value;
16
+ });
17
+ return {
18
+ path: window.location.pathname,
19
+ fullPath: window.location.pathname + window.location.search,
20
+ query: Object.keys(query).length > 0 ? query : void 0
21
+ };
22
+ }
23
+ return useMessageContextCore({
24
+ getViewport,
25
+ getRoute,
26
+ // Overlay doesn't have component selection
27
+ getComponents: () => []
28
+ });
29
+ }
@@ -0,0 +1,15 @@
1
+ import { type Ref } from 'vue';
2
+ export interface MobileSwipeOptions {
3
+ panelRef: Ref<HTMLElement | null>;
4
+ headerRef: Ref<HTMLElement | null>;
5
+ onSwipeClose: () => void;
6
+ threshold?: number;
7
+ mobileBreakpoint?: number;
8
+ }
9
+ export declare function useMobileSwipe(options: MobileSwipeOptions): {
10
+ isSwipeClosing: Ref<boolean, boolean>;
11
+ handleTouchStart: (e: TouchEvent) => void;
12
+ handleTouchEnd: () => void;
13
+ setupTouchMoveListener: () => void;
14
+ cleanupTouchMoveListener: () => void;
15
+ };