@wyxos/vibe 1.6.12 → 1.6.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,158 @@
1
+ <template>
2
+ <div class="bg-slate-900 rounded-lg overflow-hidden shadow-xl">
3
+ <!-- Tabs -->
4
+ <div class="flex items-center gap-1 bg-slate-800/50 px-4 py-2 border-b border-slate-700">
5
+ <button
6
+ v-for="tab in tabs"
7
+ :key="tab"
8
+ @click="activeTab = tab"
9
+ class="px-4 py-2 text-sm font-medium rounded-md transition-colors"
10
+ :class="activeTab === tab
11
+ ? 'bg-slate-700 text-white'
12
+ : 'text-slate-400 hover:text-slate-200 hover:bg-slate-800'"
13
+ >
14
+ {{ tab.toUpperCase() }}
15
+ </button>
16
+ <div class="flex-1"></div>
17
+ <button
18
+ @click="copyCode"
19
+ class="px-3 py-1.5 text-xs font-medium text-slate-400 hover:text-white hover:bg-slate-700 rounded-md transition-colors flex items-center gap-2"
20
+ :title="copied ? 'Copied!' : 'Copy code'"
21
+ >
22
+ <i :class="copied ? 'fas fa-check' : 'fas fa-copy'"></i>
23
+ <span>{{ copied ? 'Copied' : 'Copy' }}</span>
24
+ </button>
25
+ </div>
26
+
27
+ <!-- Code Content -->
28
+ <div class="relative">
29
+ <pre class="p-4 overflow-x-auto text-sm"><code :class="`language-${activeTab} hljs`" v-html="highlightedCode"></code></pre>
30
+ </div>
31
+ </div>
32
+ </template>
33
+
34
+ <script setup lang="ts">
35
+ import { ref, computed, watch } from 'vue'
36
+ import hljs from 'highlight.js/lib/core'
37
+ import javascript from 'highlight.js/lib/languages/javascript'
38
+ import xml from 'highlight.js/lib/languages/xml'
39
+ import css from 'highlight.js/lib/languages/css'
40
+ import 'highlight.js/styles/tokyo-night-dark.css'
41
+
42
+ // Register languages
43
+ hljs.registerLanguage('javascript', javascript)
44
+ hljs.registerLanguage('js', javascript)
45
+ hljs.registerLanguage('xml', xml)
46
+ hljs.registerLanguage('html', xml)
47
+ hljs.registerLanguage('vue', xml) // Vue uses XML/HTML highlighting
48
+ hljs.registerLanguage('css', css)
49
+
50
+ const props = defineProps<{
51
+ html?: string
52
+ js?: string
53
+ vue?: string
54
+ css?: string
55
+ }>()
56
+
57
+ const activeTab = ref<'html' | 'js' | 'vue' | 'css'>('vue')
58
+ const copied = ref(false)
59
+
60
+ const tabs = computed(() => {
61
+ const available: ('html' | 'js' | 'vue' | 'css')[] = []
62
+ if (props.html) available.push('html')
63
+ if (props.js) available.push('js')
64
+ if (props.vue) available.push('vue')
65
+ if (props.css) available.push('css')
66
+ return available
67
+ })
68
+
69
+ const currentCode = computed(() => {
70
+ switch (activeTab.value) {
71
+ case 'html': return props.html || ''
72
+ case 'js': return props.js || ''
73
+ case 'vue': return props.vue || ''
74
+ case 'css': return props.css || ''
75
+ }
76
+ })
77
+
78
+ const highlightedCode = computed(() => {
79
+ if (!currentCode.value) return ''
80
+
81
+ try {
82
+ // Map tab names to highlight.js language names
83
+ const langMap: Record<string, string> = {
84
+ 'vue': 'xml',
85
+ 'html': 'xml',
86
+ 'js': 'javascript',
87
+ 'css': 'css'
88
+ }
89
+
90
+ const lang = langMap[activeTab.value] || activeTab.value
91
+ const result = hljs.highlight(currentCode.value, { language: lang })
92
+ return result.value
93
+ } catch (error) {
94
+ console.error('Highlighting error:', error)
95
+ return escapeHtml(currentCode.value)
96
+ }
97
+ })
98
+
99
+ function escapeHtml(text: string): string {
100
+ const div = document.createElement('div')
101
+ div.textContent = text
102
+ return div.innerHTML
103
+ }
104
+
105
+ async function copyCode() {
106
+ try {
107
+ await navigator.clipboard.writeText(currentCode.value)
108
+ copied.value = true
109
+ setTimeout(() => {
110
+ copied.value = false
111
+ }, 2000)
112
+ } catch (err) {
113
+ console.error('Failed to copy:', err)
114
+ }
115
+ }
116
+
117
+ // Set initial tab to first available
118
+ watch(() => tabs.value, (newTabs) => {
119
+ if (newTabs.length > 0 && !newTabs.includes(activeTab.value)) {
120
+ activeTab.value = newTabs[0]
121
+ }
122
+ }, { immediate: true })
123
+ </script>
124
+
125
+ <style scoped>
126
+ code {
127
+ font-family: 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace;
128
+ line-height: 1.6;
129
+ }
130
+
131
+ pre {
132
+ max-height: 500px;
133
+ overflow-y: auto;
134
+ overflow-x: auto;
135
+ }
136
+
137
+ pre code {
138
+ display: block;
139
+ }
140
+
141
+ pre::-webkit-scrollbar {
142
+ width: 8px;
143
+ height: 8px;
144
+ }
145
+
146
+ pre::-webkit-scrollbar-track {
147
+ background: #1e293b;
148
+ }
149
+
150
+ pre::-webkit-scrollbar-thumb {
151
+ background: #475569;
152
+ border-radius: 4px;
153
+ }
154
+
155
+ pre::-webkit-scrollbar-thumb:hover {
156
+ background: #64748b;
157
+ }
158
+ </style>