@wyxos/vibe 1.6.12 → 1.6.13

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/lib/vibe.css CHANGED
@@ -1 +1 @@
1
- .masonry-container[data-v-919154a5]{overflow-anchor:none}.masonry-item[data-v-919154a5]{will-change:transform,opacity;contain:layout paint;transition:transform var(--masonry-duration, .45s) var(--masonry-ease, cubic-bezier(.22, .61, .36, 1)),opacity var(--masonry-leave-duration, .16s) ease-out var(--masonry-opacity-delay, 0ms);backface-visibility:hidden}.masonry-move[data-v-919154a5]{transition:transform var(--masonry-duration, .45s) var(--masonry-ease, cubic-bezier(.22, .61, .36, 1))}@media (prefers-reduced-motion: reduce){.masonry-container:not(.force-motion) .masonry-item[data-v-919154a5],.masonry-container:not(.force-motion) .masonry-move[data-v-919154a5]{transition-duration:1ms!important}}
1
+ .masonry-container[data-v-9c79a3fb]{overflow-anchor:none}.masonry-item[data-v-9c79a3fb]{will-change:transform,opacity;contain:layout paint;transition:transform var(--masonry-duration, .45s) var(--masonry-ease, cubic-bezier(.22, .61, .36, 1)),opacity var(--masonry-leave-duration, .16s) ease-out var(--masonry-opacity-delay, 0ms);backface-visibility:hidden}.masonry-move[data-v-9c79a3fb]{transition:transform var(--masonry-duration, .45s) var(--masonry-ease, cubic-bezier(.22, .61, .36, 1))}@media (prefers-reduced-motion: reduce){.masonry-container:not(.force-motion) .masonry-item[data-v-9c79a3fb],.masonry-container:not(.force-motion) .masonry-move[data-v-9c79a3fb]{transition-duration:1ms!important}}
package/package.json CHANGED
@@ -1,67 +1,93 @@
1
- {
2
- "name": "@wyxos/vibe",
3
- "version": "1.6.12",
4
- "main": "lib/index.js",
5
- "module": "lib/index.js",
6
- "types": "lib/index.d.ts",
7
- "exports": {
8
- ".": {
9
- "import": "./lib/index.js",
10
- "types": "./lib/index.d.ts"
11
- },
12
- "./vibe.css": "./lib/vibe.css",
13
- "./package.json": "./package.json"
14
- },
15
- "bin": {
16
- "vibe": "./toggle-link.mjs"
17
- },
18
- "type": "module",
19
- "files": [
20
- "lib",
21
- "src"
22
- ],
23
- "sideEffects": [
24
- "./lib/vibe.css"
25
- ],
26
- "scripts": {
27
- "dev": "vite",
28
- "build": "vue-tsc --noEmit && vite build && node write-cname.js",
29
- "build:demo": "vue-tsc --noEmit && vite build && node write-cname.js",
30
- "build:lib": "vite build --config vite.lib.config.ts && vue-tsc --declaration --emitDeclarationOnly --outDir lib",
31
- "preview": "vite preview",
32
- "watch": "vite build --watch",
33
- "release": "node release.mjs",
34
- "test": "vitest",
35
- "type-check": "vue-tsc --noEmit",
36
- "prepublishOnly": "npm run build:lib"
37
- },
38
- "dependencies": {
39
- "lodash": "^4.17.21",
40
- "lodash-es": "^4.17.21",
41
- "vue": "^3.0.0",
42
- "vue-router": "^4.6.3"
43
- },
44
- "peerDependencies": {
45
- "vue": "^3.0.0"
46
- },
47
- "devDependencies": {
48
- "@resvg/resvg-js": "^2.6.2",
49
- "@tailwindcss/vite": "^4.0.15",
50
- "@types/lodash-es": "^4.17.12",
51
- "@types/node": "^24.5.2",
52
- "@vitejs/plugin-vue": "^5.2.1",
53
- "@vue/test-utils": "^2.4.6",
54
- "chalk": "^5.3.0",
55
- "inquirer": "^10.1.8",
56
- "jsdom": "^26.0.0",
57
- "sharp": "^0.34.5",
58
- "simple-git": "^3.27.0",
59
- "tailwindcss": "^4.0.15",
60
- "typescript": "^5.9.2",
61
- "uuid": "^11.1.0",
62
- "vite": "^6.2.0",
63
- "vite-plugin-vue-mcp": "^0.3.2",
64
- "vitest": "^3.0.9",
65
- "vue-tsc": "^3.1.0"
66
- }
67
- }
1
+ {
2
+ "name": "@wyxos/vibe",
3
+ "version": "1.6.13",
4
+ "description": "A high-performance, responsive masonry layout engine for Vue 3 with built-in infinite scrolling and virtualization.",
5
+ "keywords": [
6
+ "vue",
7
+ "vue3",
8
+ "masonry",
9
+ "grid",
10
+ "layout",
11
+ "infinite-scroll",
12
+ "virtualization",
13
+ "responsive",
14
+ "performance",
15
+ "swipe",
16
+ "feed",
17
+ "mobile"
18
+ ],
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/wyxos/vibe.git"
23
+ },
24
+ "homepage": "https://wyxos.github.io/vibe/",
25
+ "bugs": {
26
+ "url": "https://github.com/wyxos/vibe/issues"
27
+ },
28
+ "main": "lib/index.js",
29
+ "module": "lib/index.js",
30
+ "types": "lib/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "import": "./lib/index.js",
34
+ "types": "./lib/index.d.ts"
35
+ },
36
+ "./vibe.css": "./lib/vibe.css",
37
+ "./package.json": "./package.json"
38
+ },
39
+ "bin": {
40
+ "vibe": "./toggle-link.mjs"
41
+ },
42
+ "type": "module",
43
+ "files": [
44
+ "lib",
45
+ "src"
46
+ ],
47
+ "sideEffects": [
48
+ "./lib/vibe.css"
49
+ ],
50
+ "scripts": {
51
+ "dev": "vite",
52
+ "build": "vue-tsc --noEmit && vite build && node write-cname.js",
53
+ "build:demo": "vue-tsc --noEmit && vite build && node write-cname.js",
54
+ "build:lib": "vite build --config vite.lib.config.ts && vue-tsc --declaration --emitDeclarationOnly --outDir lib",
55
+ "preview": "vite preview",
56
+ "watch": "vite build --watch",
57
+ "release": "node release.mjs",
58
+ "test": "vitest",
59
+ "type-check": "vue-tsc --noEmit",
60
+ "prepublishOnly": "npm run build:lib"
61
+ },
62
+ "dependencies": {
63
+ "@types/highlight.js": "^9.12.4",
64
+ "highlight.js": "^11.11.1",
65
+ "lodash": "^4.17.21",
66
+ "lodash-es": "^4.17.21",
67
+ "vue": "^3.0.0",
68
+ "vue-router": "^4.6.3"
69
+ },
70
+ "peerDependencies": {
71
+ "vue": "^3.0.0"
72
+ },
73
+ "devDependencies": {
74
+ "@resvg/resvg-js": "^2.6.2",
75
+ "@tailwindcss/vite": "^4.0.15",
76
+ "@types/lodash-es": "^4.17.12",
77
+ "@types/node": "^24.5.2",
78
+ "@vitejs/plugin-vue": "^5.2.1",
79
+ "@vue/test-utils": "^2.4.6",
80
+ "chalk": "^5.3.0",
81
+ "inquirer": "^10.1.8",
82
+ "jsdom": "^26.0.0",
83
+ "sharp": "^0.34.5",
84
+ "simple-git": "^3.27.0",
85
+ "tailwindcss": "^4.0.15",
86
+ "typescript": "^5.9.2",
87
+ "uuid": "^11.1.0",
88
+ "vite": "^6.2.0",
89
+ "vite-plugin-vue-mcp": "^0.3.2",
90
+ "vitest": "^3.0.9",
91
+ "vue-tsc": "^3.1.0"
92
+ }
93
+ }
package/src/Masonry.vue CHANGED
@@ -1137,20 +1137,9 @@ onUnmounted(() => {
1137
1137
  </div>
1138
1138
  </div>
1139
1139
 
1140
- <!-- Swipe indicator dots -->
1141
- <div v-if="masonry.length > 1" class="fixed bottom-4 left-1/2 transform -translate-x-1/2 flex gap-2 z-10 pointer-events-none">
1142
- <div
1143
- v-for="(item, index) in masonry.slice(0, Math.min(10, masonry.length))"
1144
- :key="`dot-${item.id}`"
1145
- class="w-1.5 h-1.5 rounded-full transition-all duration-300"
1146
- :class="index === currentSwipeIndex ? 'bg-white w-4' : 'bg-white/40'"
1147
- />
1148
- </div>
1149
1140
 
1150
- <!-- Item Counter -->
1151
- <div class="fixed top-20 right-4 bg-black/50 backdrop-blur-sm text-white text-xs font-medium px-3 py-1.5 rounded-full z-20 pointer-events-none">
1152
- {{ currentSwipeIndex + 1 }} / {{ masonry.length }}
1153
- </div>
1141
+
1142
+
1154
1143
  </div>
1155
1144
 
1156
1145
  <!-- Masonry Grid Mode (Desktop) -->
@@ -1174,14 +1163,7 @@ onUnmounted(() => {
1174
1163
  </div>
1175
1164
  </transition-group>
1176
1165
 
1177
- <!-- Scroll Progress Badge -->
1178
- <div v-if="containerHeight > 0"
1179
- class="fixed bottom-4 right-4 bg-gray-800 text-white text-xs rounded-full px-3 py-1.5 shadow-lg z-10 transition-opacity duration-300"
1180
- :class="{'opacity-50 hover:opacity-100': !scrollProgress.isNearTrigger, 'opacity-100': scrollProgress.isNearTrigger}">
1181
- <span>{{ masonry.length }} items</span>
1182
- <span class="mx-2">|</span>
1183
- <span>{{ scrollProgress.distanceToTrigger }}px to load</span>
1184
- </div>
1166
+
1185
1167
  </div>
1186
1168
  </div>
1187
1169
  </div>
@@ -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>
@@ -0,0 +1,45 @@
1
+ <template>
2
+ <Masonry
3
+ v-model:items="items"
4
+ :get-next-page="getPage"
5
+ :load-at-page="1"
6
+ :layout="layout"
7
+ />
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { ref } from 'vue'
12
+ import Masonry from '../../Masonry.vue'
13
+ import fixture from '../../pages.json'
14
+ import type { MasonryItem, GetPageResult } from '../../types'
15
+
16
+ const items = ref<MasonryItem[]>([])
17
+
18
+ const layout = {
19
+ sizes: { base: 1, sm: 2, md: 3, lg: 4 },
20
+ gutterX: 10,
21
+ gutterY: 10
22
+ }
23
+
24
+ const getPage = async (page: number): Promise<GetPageResult> => {
25
+ return new Promise((resolve) => {
26
+ setTimeout(() => {
27
+ const pageData = (fixture as any[])[page - 1] as { items: MasonryItem[] } | undefined
28
+
29
+ if (!pageData) {
30
+ resolve({
31
+ items: [],
32
+ nextPage: null
33
+ })
34
+ return
35
+ }
36
+
37
+ resolve({
38
+ items: pageData.items,
39
+ nextPage: page < (fixture as any[]).length ? page + 1 : null
40
+ })
41
+ }, 300)
42
+ })
43
+ }
44
+ </script>
45
+
@@ -0,0 +1,86 @@
1
+ <template>
2
+ <Masonry
3
+ v-model:items="items"
4
+ :get-next-page="getPage"
5
+ :load-at-page="1"
6
+ :layout="layout"
7
+ >
8
+ <template #item="{ item, remove }">
9
+ <div class="custom-card">
10
+ <img v-if="item.src" :src="item.src" :alt="item.title || 'Item'" />
11
+ <div class="overlay">
12
+ <h3 class="text-white font-semibold text-sm mb-2">{{ item.title || 'Untitled' }}</h3>
13
+ <button
14
+ @click="remove"
15
+ class="px-3 py-1 bg-white/20 hover:bg-white/30 border border-white/30 rounded text-white text-xs transition-colors"
16
+ >
17
+ Remove
18
+ </button>
19
+ </div>
20
+ </div>
21
+ </template>
22
+ </Masonry>
23
+ </template>
24
+
25
+ <script setup lang="ts">
26
+ import { ref } from 'vue'
27
+ import Masonry from '../../Masonry.vue'
28
+ import fixture from '../../pages.json'
29
+ import type { MasonryItem, GetPageResult } from '../../types'
30
+
31
+ const items = ref<MasonryItem[]>([])
32
+
33
+ const layout = {
34
+ sizes: { base: 1, sm: 2, md: 3, lg: 4 },
35
+ gutterX: 10,
36
+ gutterY: 10
37
+ }
38
+
39
+ const getPage = async (page: number): Promise<GetPageResult> => {
40
+ return new Promise((resolve) => {
41
+ setTimeout(() => {
42
+ const pageData = (fixture as any[])[page - 1] as { items: MasonryItem[] } | undefined
43
+
44
+ if (!pageData) {
45
+ resolve({
46
+ items: [],
47
+ nextPage: null
48
+ })
49
+ return
50
+ }
51
+
52
+ resolve({
53
+ items: pageData.items,
54
+ nextPage: page < (fixture as any[]).length ? page + 1 : null
55
+ })
56
+ }, 300)
57
+ })
58
+ }
59
+ </script>
60
+
61
+ <style scoped>
62
+ .custom-card {
63
+ position: relative;
64
+ width: 100%;
65
+ height: 100%;
66
+ border-radius: 8px;
67
+ overflow: hidden;
68
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
69
+ }
70
+
71
+ .custom-card img {
72
+ width: 100%;
73
+ height: 100%;
74
+ object-fit: cover;
75
+ }
76
+
77
+ .custom-card .overlay {
78
+ position: absolute;
79
+ bottom: 0;
80
+ left: 0;
81
+ right: 0;
82
+ background: linear-gradient(to top, rgba(0,0,0,0.8), transparent);
83
+ padding: 16px;
84
+ }
85
+ </style>
86
+
@@ -0,0 +1,39 @@
1
+ <template>
2
+ <Masonry
3
+ v-model:items="items"
4
+ :get-next-page="getPage"
5
+ :load-at-page="1"
6
+ layout-mode="swipe"
7
+ />
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { ref } from 'vue'
12
+ import Masonry from '../../Masonry.vue'
13
+ import fixture from '../../pages.json'
14
+ import type { MasonryItem, GetPageResult } from '../../types'
15
+
16
+ const items = ref<MasonryItem[]>([])
17
+
18
+ const getPage = async (page: number): Promise<GetPageResult> => {
19
+ return new Promise((resolve) => {
20
+ setTimeout(() => {
21
+ const pageData = (fixture as any[])[page - 1] as { items: MasonryItem[] } | undefined
22
+
23
+ if (!pageData) {
24
+ resolve({
25
+ items: [],
26
+ nextPage: null
27
+ })
28
+ return
29
+ }
30
+
31
+ resolve({
32
+ items: pageData.items,
33
+ nextPage: page < (fixture as any[]).length ? page + 1 : null
34
+ })
35
+ }, 300)
36
+ })
37
+ }
38
+ </script>
39
+