@wyxos/vibe 1.6.11 → 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.
@@ -1,20 +1,247 @@
1
- <template>
2
- <div class="min-h-screen bg-slate-50 p-8 pt-20">
3
- <div class="max-w-4xl mx-auto">
4
- <h1 class="text-4xl font-bold text-slate-800 mb-6">Examples</h1>
5
- <p class="text-slate-600 mb-8">This is a placeholder for examples page.</p>
6
-
7
- <div class="bg-white rounded-xl shadow-sm p-6">
8
- <h2 class="text-2xl font-semibold text-slate-800 mb-4">Coming Soon</h2>
9
- <p class="text-slate-600">
10
- Examples and code snippets will be added here soon.
11
- </p>
12
- </div>
13
- </div>
14
- </div>
15
- </template>
16
-
17
- <script setup lang="ts">
18
- // Examples page placeholder
19
- </script>
20
-
1
+ <template>
2
+ <div class="min-h-screen bg-slate-50 pt-20">
3
+ <div class="max-w-7xl mx-auto px-4 py-8">
4
+ <div class="flex gap-8">
5
+ <!-- Side Menu -->
6
+ <aside class="hidden lg:block w-64 flex-shrink-0 sticky top-24 h-fit">
7
+ <nav class="bg-white rounded-lg shadow-sm p-4 border border-slate-200">
8
+ <h3 class="text-sm font-semibold text-slate-400 uppercase tracking-wider mb-4">Examples</h3>
9
+ <ul class="space-y-2">
10
+ <li v-for="example in examples" :key="example.id">
11
+ <a
12
+ :href="`#${example.id}`"
13
+ @click.prevent="scrollTo(example.id)"
14
+ class="block px-3 py-2 text-sm rounded-md transition-colors"
15
+ :class="activeSection === example.id
16
+ ? 'bg-blue-50 text-blue-600 font-medium'
17
+ : 'text-slate-600 hover:bg-slate-50 hover:text-slate-900'"
18
+ >
19
+ {{ example.title }}
20
+ </a>
21
+ </li>
22
+ </ul>
23
+ </nav>
24
+ </aside>
25
+
26
+ <!-- Main Content -->
27
+ <main class="flex-1 min-w-0">
28
+ <div class="space-y-16">
29
+ <!-- Basic Example -->
30
+ <section id="basic" ref="basicRef" class="scroll-mt-24">
31
+ <div class="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
32
+ <div class="px-6 py-4 border-b border-slate-200 bg-slate-50">
33
+ <h2 class="text-2xl font-bold text-slate-800">Basic Usage</h2>
34
+ <p class="text-slate-600 mt-1">Simple masonry grid with default MasonryItem component</p>
35
+ </div>
36
+
37
+ <!-- Live Example -->
38
+ <div class="p-6 bg-slate-50">
39
+ <div class="bg-white rounded-lg border border-slate-200 p-4" style="height: 500px;">
40
+ <BasicExample />
41
+ </div>
42
+ </div>
43
+
44
+ <!-- Code Tabs -->
45
+ <div class="px-6 pb-6">
46
+ <CodeTabs
47
+ vue='<script setup>
48
+ import { ref } from "vue";
49
+ import { Masonry } from "@wyxos/vibe";
50
+
51
+ const items = ref([]);
52
+
53
+ async function getNextPage(page) {
54
+ const response = await fetch(`/api/items?page=${page}`);
55
+ const data = await response.json();
56
+ return {
57
+ items: data.items,
58
+ nextPage: page + 1
59
+ };
60
+ }
61
+ </script>
62
+
63
+ <template>
64
+ <Masonry
65
+ v-model:items="items"
66
+ :get-next-page="getNextPage"
67
+ :load-at-page="1"
68
+ />
69
+ </template>'
70
+ />
71
+ </div>
72
+ </div>
73
+ </section>
74
+
75
+ <!-- Custom Masonry Item Example -->
76
+ <section id="custom-item" ref="customItemRef" class="scroll-mt-24">
77
+ <div class="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
78
+ <div class="px-6 py-4 border-b border-slate-200 bg-slate-50">
79
+ <h2 class="text-2xl font-bold text-slate-800">Custom Masonry Item</h2>
80
+ <p class="text-slate-600 mt-1">Customize item rendering with scoped slots</p>
81
+ </div>
82
+
83
+ <!-- Live Example -->
84
+ <div class="p-6 bg-slate-50">
85
+ <div class="bg-white rounded-lg border border-slate-200 p-4" style="height: 500px;">
86
+ <CustomItemExample />
87
+ </div>
88
+ </div>
89
+
90
+ <!-- Code Tabs -->
91
+ <div class="px-6 pb-6">
92
+ <CodeTabs
93
+ vue='<script setup>
94
+ import { ref } from "vue";
95
+ import { Masonry } from "@wyxos/vibe";
96
+
97
+ const items = ref([]);
98
+
99
+ async function getNextPage(page) {
100
+ const response = await fetch(`/api/items?page=${page}`);
101
+ const data = await response.json();
102
+ return {
103
+ items: data.items,
104
+ nextPage: page + 1
105
+ };
106
+ }
107
+ </script>
108
+
109
+ <template>
110
+ <Masonry
111
+ v-model:items="items"
112
+ :get-next-page="getNextPage"
113
+ :load-at-page="1"
114
+ >
115
+ <template #item="{ item, remove }">
116
+ <div class="custom-card">
117
+ <img :src="item.src" :alt="item.title" />
118
+ <div class="overlay">
119
+ <h3>{{ item.title }}</h3>
120
+ <button @click="remove">Remove</button>
121
+ </div>
122
+ </div>
123
+ </template>
124
+ </Masonry>
125
+ </template>'
126
+ />
127
+ </div>
128
+ </div>
129
+ </section>
130
+
131
+ <!-- Swipe Mode Example -->
132
+ <section id="swipe-mode" ref="swipeModeRef" class="scroll-mt-24">
133
+ <div class="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
134
+ <div class="px-6 py-4 border-b border-slate-200 bg-slate-50">
135
+ <h2 class="text-2xl font-bold text-slate-800">Swipe Mode</h2>
136
+ <p class="text-slate-600 mt-1">Vertical swipe feed for mobile devices</p>
137
+ </div>
138
+
139
+ <!-- Live Example -->
140
+ <div class="p-6 bg-slate-50">
141
+ <div class="bg-white rounded-lg border border-slate-200 p-4" style="height: 500px;">
142
+ <SwipeModeExample />
143
+ </div>
144
+ </div>
145
+
146
+ <!-- Code Tabs -->
147
+ <div class="px-6 pb-6">
148
+ <CodeTabs
149
+ vue='<script setup>
150
+ import { ref } from "vue";
151
+ import { Masonry } from "@wyxos/vibe";
152
+
153
+ const items = ref([]);
154
+
155
+ async function getNextPage(page) {
156
+ const response = await fetch(`/api/items?page=${page}`);
157
+ const data = await response.json();
158
+ return {
159
+ items: data.items,
160
+ nextPage: page + 1
161
+ };
162
+ }
163
+ </script>
164
+
165
+ <template>
166
+ <!-- Auto mode: switches to swipe on mobile -->
167
+ <Masonry
168
+ v-model:items="items"
169
+ :get-next-page="getNextPage"
170
+ layout-mode="auto"
171
+ :mobile-breakpoint="768"
172
+ />
173
+
174
+ <!-- Force swipe mode on all devices -->
175
+ <Masonry
176
+ v-model:items="items"
177
+ :get-next-page="getNextPage"
178
+ layout-mode="swipe"
179
+ />
180
+ </template>'
181
+ />
182
+ </div>
183
+ </div>
184
+ </section>
185
+ </div>
186
+ </main>
187
+ </div>
188
+ </div>
189
+ </div>
190
+ </template>
191
+
192
+ <script setup lang="ts">
193
+ import { ref, onMounted, onUnmounted } from 'vue'
194
+ import CodeTabs from '../components/CodeTabs.vue'
195
+ import BasicExample from '../components/examples/BasicExample.vue'
196
+ import CustomItemExample from '../components/examples/CustomItemExample.vue'
197
+ import SwipeModeExample from '../components/examples/SwipeModeExample.vue'
198
+
199
+ const examples = [
200
+ { id: 'basic', title: 'Basic Usage' },
201
+ { id: 'custom-item', title: 'Custom Masonry Item' },
202
+ { id: 'swipe-mode', title: 'Swipe Mode' }
203
+ ]
204
+
205
+ const activeSection = ref('basic')
206
+ const basicRef = ref<HTMLElement | null>(null)
207
+ const customItemRef = ref<HTMLElement | null>(null)
208
+ const swipeModeRef = ref<HTMLElement | null>(null)
209
+
210
+ function scrollTo(id: string) {
211
+ const element = document.getElementById(id)
212
+ if (element) {
213
+ element.scrollIntoView({ behavior: 'smooth', block: 'start' })
214
+ activeSection.value = id
215
+ }
216
+ }
217
+
218
+ function updateActiveSection() {
219
+ const sections = [
220
+ { id: 'basic', ref: basicRef },
221
+ { id: 'custom-item', ref: customItemRef },
222
+ { id: 'swipe-mode', ref: swipeModeRef }
223
+ ]
224
+
225
+ const scrollPosition = window.scrollY + 100
226
+
227
+ for (let i = sections.length - 1; i >= 0; i--) {
228
+ const section = sections[i]
229
+ if (section.ref.value) {
230
+ const top = section.ref.value.offsetTop
231
+ if (scrollPosition >= top) {
232
+ activeSection.value = section.id
233
+ break
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ onMounted(() => {
240
+ window.addEventListener('scroll', updateActiveSection)
241
+ updateActiveSection()
242
+ })
243
+
244
+ onUnmounted(() => {
245
+ window.removeEventListener('scroll', updateActiveSection)
246
+ })
247
+ </script>
@@ -97,16 +97,39 @@
97
97
  </div>
98
98
  </div>
99
99
  </div>
100
+
101
+ <!-- Device Simulation -->
102
+ <div class="md:col-span-2 border-t border-slate-100 pt-6 mt-2">
103
+ <h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Device Simulation</h3>
104
+ <div class="flex flex-wrap gap-2">
105
+ <button
106
+ v-for="mode in ['auto', 'phone', 'tablet', 'desktop']"
107
+ :key="mode"
108
+ @click="deviceMode = mode as any"
109
+ class="px-4 py-2 rounded-lg text-sm font-medium transition-all capitalize"
110
+ :class="deviceMode === mode ? 'bg-blue-600 text-white shadow-md shadow-blue-200' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'"
111
+ >
112
+ <i class="fas mr-2" :class="{
113
+ 'fa-desktop': mode === 'desktop' || mode === 'auto',
114
+ 'fa-mobile-alt': mode === 'phone',
115
+ 'fa-tablet-alt': mode === 'tablet'
116
+ }"></i>
117
+ {{ mode }}
118
+ </button>
119
+ </div>
120
+ </div>
100
121
  </div>
101
122
  </div>
102
123
  </transition>
103
124
  </header>
104
125
 
105
126
  <!-- Main Content -->
106
- <div class="flex flex-1 overflow-hidden relative p-5">
107
- <masonry v-model:items="items" :get-next-page="getPage" :load-at-page="1" :layout="layout" ref="masonry">
108
- <!-- MasonryItem is used automatically, but you can customize it -->
109
- </masonry>
127
+ <div class="flex flex-1 overflow-hidden relative p-5 transition-all duration-300 ease-in-out" :class="{'bg-slate-200/50': deviceMode !== 'auto'}">
128
+ <div :style="containerStyle" class="transition-all duration-500 ease-in-out bg-slate-50 shadow-sm relative">
129
+ <masonry v-model:items="items" :get-next-page="getPage" :load-at-page="1" :layout="layout" ref="masonry">
130
+ <!-- MasonryItem is used automatically, but you can customize it -->
131
+ </masonry>
132
+ </div>
110
133
  </div>
111
134
  </main>
112
135
  </template>
@@ -166,4 +189,20 @@ const getPage = async (page: number): Promise<GetPageResult> => {
166
189
  }, 1000);
167
190
  });
168
191
  };
192
+
193
+ // Device Simulation
194
+ const deviceMode = ref<'auto' | 'phone' | 'tablet' | 'desktop'>('auto');
195
+
196
+ const containerStyle = computed(() => {
197
+ switch (deviceMode.value) {
198
+ case 'phone':
199
+ return { width: '375px', maxWidth: '100%', margin: '0 auto', border: '1px solid #e2e8f0', borderRadius: '20px', overflow: 'hidden', height: '100%' };
200
+ case 'tablet':
201
+ return { width: '768px', maxWidth: '100%', margin: '0 auto', border: '1px solid #e2e8f0', borderRadius: '12px', overflow: 'hidden', height: '100%' };
202
+ case 'desktop':
203
+ return { width: '1280px', maxWidth: '100%', margin: '0 auto', border: '1px solid #e2e8f0', borderRadius: '8px', overflow: 'hidden', height: '100%' };
204
+ default:
205
+ return { width: '100%', height: '100%' };
206
+ }
207
+ });
169
208
  </script>
package/toggle-link.mjs CHANGED
@@ -1,92 +1,92 @@
1
- #!/usr/bin/env node
2
-
3
- import { readFileSync, writeFileSync } from 'fs';
4
- import { resolve, dirname, relative } from 'path';
5
- import { fileURLToPath } from 'url';
6
- import { execSync } from 'child_process';
7
-
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = dirname(__filename);
10
-
11
- // Get the vibe package directory
12
- const vibeDir = resolve(__dirname);
13
- const vibePackageJson = JSON.parse(readFileSync(resolve(vibeDir, 'package.json'), 'utf-8'));
14
- const vibeVersion = vibePackageJson.version;
15
-
16
- // Get the current working directory (where the script is run from)
17
- const cwd = process.cwd();
18
- const targetPackageJsonPath = resolve(cwd, 'package.json');
19
-
20
- try {
21
- const targetPackageJson = JSON.parse(readFileSync(targetPackageJsonPath, 'utf-8'));
22
-
23
- // Check if @wyxos/vibe exists in dependencies or devDependencies
24
- const deps = { ...targetPackageJson.dependencies, ...targetPackageJson.devDependencies };
25
- const currentVibeRef = deps['@wyxos/vibe'];
26
-
27
- if (!currentVibeRef) {
28
- console.error('❌ @wyxos/vibe not found in package.json dependencies or devDependencies');
29
- process.exit(1);
30
- }
31
-
32
- // Determine if it's a local path or version reference
33
- const isLocalPath = currentVibeRef.startsWith('file:') || currentVibeRef.startsWith('../') || currentVibeRef.startsWith('./');
34
-
35
- let newRef;
36
- let action;
37
-
38
- if (isLocalPath) {
39
- // Switch to published version
40
- newRef = `^${vibeVersion}`;
41
- action = 'published version';
42
- } else {
43
- // Switch to local path
44
- // Calculate relative path from target to vibe
45
- const relativePath = relative(cwd, vibeDir).replace(/\\/g, '/');
46
- newRef = `file:${relativePath}`;
47
- action = 'local path';
48
- }
49
-
50
- // Update package.json
51
- if (targetPackageJson.dependencies && targetPackageJson.dependencies['@wyxos/vibe']) {
52
- targetPackageJson.dependencies['@wyxos/vibe'] = newRef;
53
- }
54
- if (targetPackageJson.devDependencies && targetPackageJson.devDependencies['@wyxos/vibe']) {
55
- targetPackageJson.devDependencies['@wyxos/vibe'] = newRef;
56
- }
57
-
58
- // Write updated package.json
59
- writeFileSync(
60
- targetPackageJsonPath,
61
- JSON.stringify(targetPackageJson, null, 2) + '\n',
62
- 'utf-8'
63
- );
64
-
65
- console.log(`✅ Switched @wyxos/vibe to ${action}: ${newRef}`);
66
-
67
- // If switching to local path, ensure the library is built before installation
68
- if (action === 'local path') {
69
- try {
70
- console.log('🔧 Building local @wyxos/vibe (build:lib)...');
71
- execSync('npm run build:lib', { cwd: vibeDir, stdio: 'inherit' });
72
- } catch (e) {
73
- console.warn('⚠️ Failed to build local @wyxos/vibe. Attempting install anyway.');
74
- }
75
- }
76
-
77
- // Run npm install
78
- console.log('📦 Running npm install...');
79
- execSync('npm install', { cwd, stdio: 'inherit' });
80
-
81
- console.log('✨ Done!');
82
-
83
- } catch (error) {
84
- if (error.code === 'ENOENT') {
85
- console.error(`❌ package.json not found in ${cwd}`);
86
- console.error(' Make sure you run this script from a project directory that uses @wyxos/vibe');
87
- } else {
88
- console.error('❌ Error:', error.message);
89
- }
90
- process.exit(1);
91
- }
92
-
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, writeFileSync } from 'fs';
4
+ import { resolve, dirname, relative } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { execSync } from 'child_process';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+
11
+ // Get the vibe package directory
12
+ const vibeDir = resolve(__dirname);
13
+ const vibePackageJson = JSON.parse(readFileSync(resolve(vibeDir, 'package.json'), 'utf-8'));
14
+ const vibeVersion = vibePackageJson.version;
15
+
16
+ // Get the current working directory (where the script is run from)
17
+ const cwd = process.cwd();
18
+ const targetPackageJsonPath = resolve(cwd, 'package.json');
19
+
20
+ try {
21
+ const targetPackageJson = JSON.parse(readFileSync(targetPackageJsonPath, 'utf-8'));
22
+
23
+ // Check if @wyxos/vibe exists in dependencies or devDependencies
24
+ const deps = { ...targetPackageJson.dependencies, ...targetPackageJson.devDependencies };
25
+ const currentVibeRef = deps['@wyxos/vibe'];
26
+
27
+ if (!currentVibeRef) {
28
+ console.error('❌ @wyxos/vibe not found in package.json dependencies or devDependencies');
29
+ process.exit(1);
30
+ }
31
+
32
+ // Determine if it's a local path or version reference
33
+ const isLocalPath = currentVibeRef.startsWith('file:') || currentVibeRef.startsWith('../') || currentVibeRef.startsWith('./');
34
+
35
+ let newRef;
36
+ let action;
37
+
38
+ if (isLocalPath) {
39
+ // Switch to published version
40
+ newRef = `^${vibeVersion}`;
41
+ action = 'published version';
42
+ } else {
43
+ // Switch to local path
44
+ // Calculate relative path from target to vibe
45
+ const relativePath = relative(cwd, vibeDir).replace(/\\/g, '/');
46
+ newRef = `file:${relativePath}`;
47
+ action = 'local path';
48
+ }
49
+
50
+ // Update package.json
51
+ if (targetPackageJson.dependencies && targetPackageJson.dependencies['@wyxos/vibe']) {
52
+ targetPackageJson.dependencies['@wyxos/vibe'] = newRef;
53
+ }
54
+ if (targetPackageJson.devDependencies && targetPackageJson.devDependencies['@wyxos/vibe']) {
55
+ targetPackageJson.devDependencies['@wyxos/vibe'] = newRef;
56
+ }
57
+
58
+ // Write updated package.json
59
+ writeFileSync(
60
+ targetPackageJsonPath,
61
+ JSON.stringify(targetPackageJson, null, 2) + '\n',
62
+ 'utf-8'
63
+ );
64
+
65
+ console.log(`✅ Switched @wyxos/vibe to ${action}: ${newRef}`);
66
+
67
+ // If switching to local path, ensure the library is built before installation
68
+ if (action === 'local path') {
69
+ try {
70
+ console.log('🔧 Building local @wyxos/vibe (build:lib)...');
71
+ execSync('npm run build:lib', { cwd: vibeDir, stdio: 'inherit' });
72
+ } catch (e) {
73
+ console.warn('⚠️ Failed to build local @wyxos/vibe. Attempting install anyway.');
74
+ }
75
+ }
76
+
77
+ // Run npm install
78
+ console.log('📦 Running npm install...');
79
+ execSync('npm install', { cwd, stdio: 'inherit' });
80
+
81
+ console.log('✨ Done!');
82
+
83
+ } catch (error) {
84
+ if (error.code === 'ENOENT') {
85
+ console.error(`❌ package.json not found in ${cwd}`);
86
+ console.error(' Make sure you run this script from a project directory that uses @wyxos/vibe');
87
+ } else {
88
+ console.error('❌ Error:', error.message);
89
+ }
90
+ process.exit(1);
91
+ }
92
+