@linktr.ee/linkapp 0.0.21 → 0.0.23
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/dev-server/README.md +5 -5
- package/dev-server/components/SettingsPreview.tsx +330 -0
- package/dev-server/components/ui/dialog.tsx +6 -3
- package/dev-server/env.d.ts +8 -0
- package/dev-server/featured/main.tsx +48 -43
- package/dev-server/featured.html +21 -35
- package/dev-server/index.html +8 -1
- package/dev-server/lib/utils.ts +6 -0
- package/dev-server/package-lock.json +245 -287
- package/dev-server/package.json +7 -4
- package/dev-server/postcss/tailwind-source-fallback.js +94 -0
- package/dev-server/postcss.config.mjs +30 -0
- package/dev-server/preview/Preview.tsx +182 -381
- package/dev-server/preview/preview.css +22 -34
- package/dev-server/public/apple-touch-icon.png +0 -0
- package/dev-server/public/favicon-96x96.png +0 -0
- package/dev-server/public/favicon.ico +0 -0
- package/dev-server/public/favicon.svg +6 -0
- package/dev-server/public/site.webmanifest +21 -0
- package/dev-server/public/web-app-manifest-192x192.png +0 -0
- package/dev-server/public/web-app-manifest-512x512.png +0 -0
- package/dev-server/rsbuild.config.ts +45 -0
- package/dev-server/shared/theme-presets.ts +315 -0
- package/dev-server/shared/theme-utils.ts +38 -0
- package/dev-server/sheet/main.tsx +44 -0
- package/dev-server/sheet.html +56 -0
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +20 -12
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +55 -36
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +74 -59
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +564 -43
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +19 -9
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +9 -4
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/test-url-match-rules.d.ts.map +1 -1
- package/dist/commands/test-url-match-rules.js +24 -13
- package/dist/commands/test-url-match-rules.js.map +1 -1
- package/dist/components/index.d.ts +0 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +2 -1
- package/dist/components/index.js.map +1 -1
- package/dist/lib/auth/device-flow.d.ts +1 -6
- package/dist/lib/auth/device-flow.d.ts.map +1 -1
- package/dist/lib/auth/device-flow.js +3 -9
- package/dist/lib/auth/device-flow.js.map +1 -1
- package/dist/lib/auth/token-storage.d.ts +0 -5
- package/dist/lib/auth/token-storage.d.ts.map +1 -1
- package/dist/lib/auth/token-storage.js +16 -14
- package/dist/lib/auth/token-storage.js.map +1 -1
- package/dist/lib/build/detect-layouts.d.ts +1 -1
- package/dist/lib/build/detect-layouts.d.ts.map +1 -1
- package/dist/lib/build/detect-layouts.js +8 -7
- package/dist/lib/build/detect-layouts.js.map +1 -1
- package/dist/lib/deploy/generate-manifest-files.js +1 -1
- package/dist/lib/deploy/generate-manifest-files.js.map +1 -1
- package/dist/lib/deploy/pack-project.js +2 -2
- package/dist/lib/deploy/pack-project.js.map +1 -1
- package/dist/lib/deploy/test-url-match-rules.d.ts +2 -1
- package/dist/lib/deploy/test-url-match-rules.d.ts.map +1 -1
- package/dist/lib/deploy/test-url-match-rules.js +1 -1
- package/dist/lib/deploy/test-url-match-rules.js.map +1 -1
- package/dist/lib/deploy/upload.d.ts.map +1 -1
- package/dist/lib/deploy/upload.js +7 -3
- package/dist/lib/deploy/upload.js.map +1 -1
- package/dist/lib/deploy/validation.d.ts.map +1 -1
- package/dist/lib/deploy/validation.js +8 -5
- package/dist/lib/deploy/validation.js.map +1 -1
- package/dist/lib/rsbuild/config-factory.d.ts +24 -0
- package/dist/lib/rsbuild/config-factory.d.ts.map +1 -0
- package/dist/lib/rsbuild/config-factory.js +135 -0
- package/dist/lib/rsbuild/config-factory.js.map +1 -0
- package/dist/lib/rsbuild/plugins/asset-versioning.d.ts +11 -0
- package/dist/lib/rsbuild/plugins/asset-versioning.d.ts.map +1 -0
- package/dist/lib/rsbuild/plugins/asset-versioning.js +62 -0
- package/dist/lib/rsbuild/plugins/asset-versioning.js.map +1 -0
- package/dist/lib/rsbuild/plugins/copy-public.d.ts +11 -0
- package/dist/lib/rsbuild/plugins/copy-public.d.ts.map +1 -0
- package/dist/lib/rsbuild/plugins/copy-public.js +32 -0
- package/dist/lib/rsbuild/plugins/copy-public.js.map +1 -0
- package/dist/lib/rsbuild/postcss/tailwind-source-fallback.d.ts +12 -0
- package/dist/lib/rsbuild/postcss/tailwind-source-fallback.d.ts.map +1 -0
- package/dist/lib/rsbuild/postcss/tailwind-source-fallback.js +60 -0
- package/dist/lib/rsbuild/postcss/tailwind-source-fallback.js.map +1 -0
- package/dist/lib/utils/setup-runtime.d.ts.map +1 -1
- package/dist/lib/utils/setup-runtime.js +78 -17
- package/dist/lib/utils/setup-runtime.js.map +1 -1
- package/dist/lib/vite/config-factory.d.ts.map +1 -1
- package/dist/lib/vite/config-factory.js +8 -3
- package/dist/lib/vite/config-factory.js.map +1 -1
- package/dist/lib/vite/plugins/copy-public.d.ts +12 -0
- package/dist/lib/vite/plugins/copy-public.d.ts.map +1 -0
- package/dist/lib/vite/plugins/copy-public.js +31 -0
- package/dist/lib/vite/plugins/copy-public.js.map +1 -0
- package/dist/schema/config.schema.d.ts +121 -32
- package/dist/schema/config.schema.d.ts.map +1 -1
- package/dist/schema/config.schema.js +28 -17
- package/dist/schema/config.schema.js.map +1 -1
- package/dist/types.d.ts +6 -57
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +7 -4
- package/runtime/index.html +19 -36
- package/dev-server/classic/main.tsx +0 -62
- package/dev-server/classic.html +0 -70
- package/dev-server/vite.config.ts +0 -29
package/dev-server/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Development Server Setup
|
|
2
2
|
|
|
3
|
-
This directory contains a custom
|
|
3
|
+
This directory contains a custom Rsbuild-based development server for previewing LinkApp components with multiple routes and iframe embeds.
|
|
4
4
|
|
|
5
5
|
## Structure
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
dev-server/
|
|
9
9
|
├── index.html # Root preview page entry
|
|
10
|
-
├──
|
|
10
|
+
├── rsbuild.config.ts # Rsbuild configuration
|
|
11
11
|
├── server.ts # TypeScript server launcher
|
|
12
12
|
├── preview/
|
|
13
13
|
│ ├── main.tsx # Preview page React entry
|
|
@@ -44,12 +44,12 @@ dev-server/
|
|
|
44
44
|
npm run dev:preview
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
This command uses `tsx` to run the TypeScript server script, which programmatically starts
|
|
47
|
+
This command uses `tsx` to run the TypeScript server script, which programmatically starts Rsbuild with the custom configuration.
|
|
48
48
|
|
|
49
49
|
## Technical Details
|
|
50
50
|
|
|
51
51
|
### Multi-Page Application (MPA)
|
|
52
|
-
The setup uses
|
|
52
|
+
The setup uses Rsbuild's MPA capability with multiple HTML entry points:
|
|
53
53
|
- Root: `index.html` → Preview page
|
|
54
54
|
- `/classic/`: `classic/index.html` → Classic layout
|
|
55
55
|
- `/featured/`: `featured/index.html` → Featured layout
|
|
@@ -77,6 +77,6 @@ Server runs on port 3000 with `strictPort: true` to match requirements.
|
|
|
77
77
|
This implementation follows a clean separation of concerns:
|
|
78
78
|
- Original app files (`1-demo/app/*`) remain untouched
|
|
79
79
|
- New dev-server directory contains all preview infrastructure
|
|
80
|
-
- Uses
|
|
80
|
+
- Uses Rsbuild's native MPA support for clean routing (no client-side router)
|
|
81
81
|
- TypeScript throughout for type safety
|
|
82
82
|
- Programmatic server creation for flexibility
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { LinkAppSettings, SettingsElement } from "../../src/types";
|
|
2
|
+
|
|
3
|
+
interface SettingsPreviewProps {
|
|
4
|
+
settings: LinkAppSettings;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function SettingsPreview({ settings }: SettingsPreviewProps) {
|
|
8
|
+
return (
|
|
9
|
+
<div className="max-w-4xl mx-auto p-6 space-y-6">
|
|
10
|
+
{/* Header */}
|
|
11
|
+
<div className="space-y-2">
|
|
12
|
+
<h2 className="text-2xl font-bold">{settings.title}</h2>
|
|
13
|
+
{settings.overview && (
|
|
14
|
+
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
|
|
15
|
+
{settings.overview.title && (
|
|
16
|
+
<h3 className="font-semibold text-blue-900 mb-1">
|
|
17
|
+
{settings.overview.title}
|
|
18
|
+
</h3>
|
|
19
|
+
)}
|
|
20
|
+
<p className="text-blue-800 text-sm">
|
|
21
|
+
{settings.overview.description}
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
)}
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
{/* Metadata */}
|
|
28
|
+
<div className="grid grid-cols-2 gap-4 bg-gray-50 border border-gray-200 rounded-lg p-4">
|
|
29
|
+
<div>
|
|
30
|
+
<span className="text-sm font-medium text-gray-600">Uses URL:</span>
|
|
31
|
+
<span className="ml-2 text-sm">
|
|
32
|
+
{settings.uses_url || settings.has_url ? "Yes" : "No"}
|
|
33
|
+
</span>
|
|
34
|
+
</div>
|
|
35
|
+
<div>
|
|
36
|
+
<span className="text-sm font-medium text-gray-600">
|
|
37
|
+
Supports Featured Layout:
|
|
38
|
+
</span>
|
|
39
|
+
<span className="ml-2 text-sm">
|
|
40
|
+
{settings.supports_featured_layout ? "Yes" : "No"}
|
|
41
|
+
</span>
|
|
42
|
+
</div>
|
|
43
|
+
{settings.icon && (
|
|
44
|
+
<div>
|
|
45
|
+
<span className="text-sm font-medium text-gray-600">Icon:</span>
|
|
46
|
+
<span className="ml-2 text-sm">{settings.icon}</span>
|
|
47
|
+
</div>
|
|
48
|
+
)}
|
|
49
|
+
{settings.settings_tab_title && (
|
|
50
|
+
<div>
|
|
51
|
+
<span className="text-sm font-medium text-gray-600">
|
|
52
|
+
Settings Tab Title:
|
|
53
|
+
</span>
|
|
54
|
+
<span className="ml-2 text-sm">{settings.settings_tab_title}</span>
|
|
55
|
+
</div>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
{/* Setup Instructions */}
|
|
60
|
+
{settings.setup_instructions && (
|
|
61
|
+
<div className="bg-green-50 border border-green-200 rounded-lg p-4">
|
|
62
|
+
{settings.setup_instructions.title && (
|
|
63
|
+
<h3 className="font-semibold text-green-900 mb-1">
|
|
64
|
+
{settings.setup_instructions.title}
|
|
65
|
+
</h3>
|
|
66
|
+
)}
|
|
67
|
+
<p className="text-green-800 text-sm">
|
|
68
|
+
{settings.setup_instructions.description}
|
|
69
|
+
</p>
|
|
70
|
+
</div>
|
|
71
|
+
)}
|
|
72
|
+
|
|
73
|
+
{/* Settings Elements */}
|
|
74
|
+
<div className="space-y-4">
|
|
75
|
+
<h3 className="text-xl font-semibold">Settings Elements</h3>
|
|
76
|
+
{settings.elements.length === 0 ? (
|
|
77
|
+
<p className="text-gray-500 italic">No settings elements defined</p>
|
|
78
|
+
) : (
|
|
79
|
+
<div className="space-y-4">
|
|
80
|
+
{settings.elements.map((element, index) => (
|
|
81
|
+
<SettingsElementCard key={element.id} element={element} index={index} />
|
|
82
|
+
))}
|
|
83
|
+
</div>
|
|
84
|
+
)}
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface SettingsElementCardProps {
|
|
91
|
+
element: SettingsElement;
|
|
92
|
+
index: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function SettingsElementCard({ element, index }: SettingsElementCardProps) {
|
|
96
|
+
return (
|
|
97
|
+
<div className="border border-gray-300 rounded-lg p-4 bg-white shadow-sm">
|
|
98
|
+
<div className="flex items-start justify-between mb-3">
|
|
99
|
+
<div className="flex-1">
|
|
100
|
+
<div className="flex items-center gap-2">
|
|
101
|
+
<span className="text-xs font-mono bg-gray-100 px-2 py-1 rounded">
|
|
102
|
+
{index + 1}
|
|
103
|
+
</span>
|
|
104
|
+
<h4 className="font-semibold text-lg">{element.title || element.label}</h4>
|
|
105
|
+
</div>
|
|
106
|
+
<p className="text-sm text-gray-600 mt-1">
|
|
107
|
+
<span className="font-mono text-xs bg-blue-100 text-blue-800 px-1.5 py-0.5 rounded">
|
|
108
|
+
{element.id}
|
|
109
|
+
</span>
|
|
110
|
+
</p>
|
|
111
|
+
</div>
|
|
112
|
+
<span className="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-purple-100 text-purple-800">
|
|
113
|
+
{element.inputType}
|
|
114
|
+
</span>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
{element.description && (
|
|
118
|
+
<p className="text-sm text-gray-700 mb-3">{element.description}</p>
|
|
119
|
+
)}
|
|
120
|
+
|
|
121
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 text-sm">
|
|
122
|
+
{element.placeholder && (
|
|
123
|
+
<div>
|
|
124
|
+
<span className="font-medium text-gray-600">Placeholder:</span>
|
|
125
|
+
<span className="ml-2 text-gray-800">{element.placeholder}</span>
|
|
126
|
+
</div>
|
|
127
|
+
)}
|
|
128
|
+
|
|
129
|
+
{element.defaultValue !== undefined && (
|
|
130
|
+
<div>
|
|
131
|
+
<span className="font-medium text-gray-600">Default Value:</span>
|
|
132
|
+
<span className="ml-2 text-gray-800 font-mono text-xs">
|
|
133
|
+
{typeof element.defaultValue === "boolean"
|
|
134
|
+
? element.defaultValue.toString()
|
|
135
|
+
: Array.isArray(element.defaultValue)
|
|
136
|
+
? `[${element.defaultValue.join(", ")}]`
|
|
137
|
+
: element.defaultValue}
|
|
138
|
+
</span>
|
|
139
|
+
</div>
|
|
140
|
+
)}
|
|
141
|
+
|
|
142
|
+
{element.validation && (
|
|
143
|
+
<div className="col-span-2">
|
|
144
|
+
<span className="font-medium text-gray-600">Validation:</span>
|
|
145
|
+
<div className="ml-2 mt-1 space-y-1">
|
|
146
|
+
{element.validation.required && (
|
|
147
|
+
<div className="text-xs">
|
|
148
|
+
<span className="bg-red-100 text-red-800 px-2 py-0.5 rounded">
|
|
149
|
+
Required
|
|
150
|
+
</span>
|
|
151
|
+
</div>
|
|
152
|
+
)}
|
|
153
|
+
{element.validation.minLength !== undefined && (
|
|
154
|
+
<div className="text-xs text-gray-600">
|
|
155
|
+
Min Length: {element.validation.minLength}
|
|
156
|
+
</div>
|
|
157
|
+
)}
|
|
158
|
+
{element.validation.maxLength !== undefined && (
|
|
159
|
+
<div className="text-xs text-gray-600">
|
|
160
|
+
Max Length: {element.validation.maxLength}
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
{element.validation.min !== undefined && (
|
|
164
|
+
<div className="text-xs text-gray-600">
|
|
165
|
+
Min: {element.validation.min}
|
|
166
|
+
</div>
|
|
167
|
+
)}
|
|
168
|
+
{element.validation.max !== undefined && (
|
|
169
|
+
<div className="text-xs text-gray-600">
|
|
170
|
+
Max: {element.validation.max}
|
|
171
|
+
</div>
|
|
172
|
+
)}
|
|
173
|
+
{element.validation.maxSize !== undefined && (
|
|
174
|
+
<div className="text-xs text-gray-600">
|
|
175
|
+
Max Size: {element.validation.maxSize}
|
|
176
|
+
</div>
|
|
177
|
+
)}
|
|
178
|
+
{element.validation.pattern && (
|
|
179
|
+
<div className="text-xs text-gray-600 font-mono">
|
|
180
|
+
Pattern: {element.validation.pattern}
|
|
181
|
+
</div>
|
|
182
|
+
)}
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
)}
|
|
186
|
+
|
|
187
|
+
{element.options && element.options.length > 0 && (
|
|
188
|
+
<div className="col-span-2">
|
|
189
|
+
<span className="font-medium text-gray-600">Options:</span>
|
|
190
|
+
<div className="ml-2 mt-1 space-y-1">
|
|
191
|
+
{element.options.map((option) => (
|
|
192
|
+
<div key={option.value} className="text-xs bg-gray-50 px-2 py-1 rounded">
|
|
193
|
+
<span className="font-mono text-blue-700">{option.value}</span>
|
|
194
|
+
<span className="mx-1 text-gray-400">→</span>
|
|
195
|
+
<span>{option.label}</span>
|
|
196
|
+
</div>
|
|
197
|
+
))}
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
)}
|
|
201
|
+
|
|
202
|
+
{element.linkBehaviorLabels && (
|
|
203
|
+
<div className="col-span-2">
|
|
204
|
+
<span className="font-medium text-gray-600">Link Behavior Labels:</span>
|
|
205
|
+
<div className="ml-2 mt-1 space-y-1">
|
|
206
|
+
<div className="text-xs">
|
|
207
|
+
<span className="font-medium">Embed:</span> {element.linkBehaviorLabels.embedLabel}
|
|
208
|
+
</div>
|
|
209
|
+
<div className="text-xs">
|
|
210
|
+
<span className="font-medium">Link Off:</span> {element.linkBehaviorLabels.linkOffLabel}
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
)}
|
|
215
|
+
|
|
216
|
+
{element.accept && element.accept.length > 0 && (
|
|
217
|
+
<div className="col-span-2">
|
|
218
|
+
<span className="font-medium text-gray-600">Accepted Files:</span>
|
|
219
|
+
<span className="ml-2 text-xs font-mono">
|
|
220
|
+
{element.accept.join(", ")}
|
|
221
|
+
</span>
|
|
222
|
+
</div>
|
|
223
|
+
)}
|
|
224
|
+
|
|
225
|
+
{element.multiple && (
|
|
226
|
+
<div>
|
|
227
|
+
<span className="bg-orange-100 text-orange-800 px-2 py-0.5 rounded text-xs">
|
|
228
|
+
Multiple values allowed
|
|
229
|
+
</span>
|
|
230
|
+
</div>
|
|
231
|
+
)}
|
|
232
|
+
|
|
233
|
+
{element.capability && (
|
|
234
|
+
<div>
|
|
235
|
+
<span className="font-medium text-gray-600">Integration Capability:</span>
|
|
236
|
+
<span className="ml-2 text-xs">{element.capability}</span>
|
|
237
|
+
</div>
|
|
238
|
+
)}
|
|
239
|
+
|
|
240
|
+
{element.vendor && (
|
|
241
|
+
<div>
|
|
242
|
+
<span className="font-medium text-gray-600">Vendor:</span>
|
|
243
|
+
<span className="ml-2 text-xs">{element.vendor}</span>
|
|
244
|
+
</div>
|
|
245
|
+
)}
|
|
246
|
+
|
|
247
|
+
{element.array_options && (
|
|
248
|
+
<div className="col-span-2">
|
|
249
|
+
<span className="font-medium text-gray-600">Array Options:</span>
|
|
250
|
+
<div className="ml-2 mt-1 space-y-1 text-xs">
|
|
251
|
+
{element.array_options.add_item_button_text && (
|
|
252
|
+
<div>Add Button: "{element.array_options.add_item_button_text}"</div>
|
|
253
|
+
)}
|
|
254
|
+
{element.array_options.add_item_title && (
|
|
255
|
+
<div>Add Title: "{element.array_options.add_item_title}"</div>
|
|
256
|
+
)}
|
|
257
|
+
{element.array_options.edit_item_title && (
|
|
258
|
+
<div>Edit Title: "{element.array_options.edit_item_title}"</div>
|
|
259
|
+
)}
|
|
260
|
+
{element.array_options.item_format && (
|
|
261
|
+
<div>Item Format: "{element.array_options.item_format}"</div>
|
|
262
|
+
)}
|
|
263
|
+
{element.array_options.min !== undefined && (
|
|
264
|
+
<div>Min Items: {element.array_options.min}</div>
|
|
265
|
+
)}
|
|
266
|
+
{element.array_options.max !== undefined && (
|
|
267
|
+
<div>Max Items: {element.array_options.max}</div>
|
|
268
|
+
)}
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
)}
|
|
272
|
+
|
|
273
|
+
{element.array_elements && element.array_elements.length > 0 && (
|
|
274
|
+
<div className="col-span-2">
|
|
275
|
+
<span className="font-medium text-gray-600">Array Elements:</span>
|
|
276
|
+
<div className="ml-2 mt-2 space-y-2">
|
|
277
|
+
{element.array_elements.map((arrayElement, arrayIndex) => (
|
|
278
|
+
<div key={arrayElement.id} className="bg-gray-50 border border-gray-200 rounded p-3">
|
|
279
|
+
<div className="text-xs font-mono text-blue-700 mb-1">
|
|
280
|
+
{arrayElement.id}
|
|
281
|
+
</div>
|
|
282
|
+
<div className="text-xs">
|
|
283
|
+
<span className="font-medium">Type:</span> {arrayElement.inputType}
|
|
284
|
+
</div>
|
|
285
|
+
{arrayElement.label && (
|
|
286
|
+
<div className="text-xs">
|
|
287
|
+
<span className="font-medium">Label:</span> {arrayElement.label}
|
|
288
|
+
</div>
|
|
289
|
+
)}
|
|
290
|
+
{arrayElement.placeholder && (
|
|
291
|
+
<div className="text-xs">
|
|
292
|
+
<span className="font-medium">Placeholder:</span> {arrayElement.placeholder}
|
|
293
|
+
</div>
|
|
294
|
+
)}
|
|
295
|
+
</div>
|
|
296
|
+
))}
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
)}
|
|
300
|
+
|
|
301
|
+
{element.conditionalDisplay && (
|
|
302
|
+
<div className="col-span-2">
|
|
303
|
+
<span className="font-medium text-gray-600">Conditional Display:</span>
|
|
304
|
+
<div className="ml-2 mt-1 text-xs bg-yellow-50 border border-yellow-200 rounded p-2">
|
|
305
|
+
Depends on <span className="font-mono">{element.conditionalDisplay.dependsOn}</span>
|
|
306
|
+
{" "}
|
|
307
|
+
{element.conditionalDisplay.operator || "equals"}
|
|
308
|
+
{" "}
|
|
309
|
+
<span className="font-mono">{String(element.conditionalDisplay.value)}</span>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
)}
|
|
313
|
+
|
|
314
|
+
{element.action && (
|
|
315
|
+
<div className="col-span-2">
|
|
316
|
+
<span className="font-medium text-gray-600">Action:</span>
|
|
317
|
+
<div className="ml-2 mt-1 text-xs">
|
|
318
|
+
On {element.action.on}: {element.action.type}
|
|
319
|
+
{element.action.data && (
|
|
320
|
+
<div className="font-mono text-xs mt-1 bg-gray-50 p-1 rounded">
|
|
321
|
+
{JSON.stringify(element.action.data, null, 2)}
|
|
322
|
+
</div>
|
|
323
|
+
)}
|
|
324
|
+
</div>
|
|
325
|
+
</div>
|
|
326
|
+
)}
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
@@ -45,12 +45,15 @@ function DialogContent({
|
|
|
45
45
|
<DialogPrimitive.Content
|
|
46
46
|
data-slot="dialog-content"
|
|
47
47
|
className={cn(
|
|
48
|
-
'bg-background fixed top-[50%] left-[50%] z-50
|
|
48
|
+
'bg-background fixed top-[50%] left-[50%] z-50 w-full translate-x-[-50%] translate-y-[-50%] rounded-3xl duration-200',
|
|
49
49
|
className
|
|
50
50
|
)}
|
|
51
51
|
{...props}
|
|
52
52
|
>
|
|
53
|
-
|
|
53
|
+
<div className="flex h-full flex-col">
|
|
54
|
+
{children}
|
|
55
|
+
</div>
|
|
56
|
+
|
|
54
57
|
{showCloseButton && (
|
|
55
58
|
<DialogPrimitive.Close
|
|
56
59
|
data-slot="dialog-close"
|
|
@@ -69,7 +72,7 @@ function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
69
72
|
return (
|
|
70
73
|
<div
|
|
71
74
|
data-slot="dialog-header"
|
|
72
|
-
className={cn(
|
|
75
|
+
className={cn(className)}
|
|
73
76
|
{...props}
|
|
74
77
|
/>
|
|
75
78
|
)
|
|
@@ -2,61 +2,66 @@ import Featured from "@/app/featured";
|
|
|
2
2
|
import { StrictMode } from "react";
|
|
3
3
|
import { createRoot } from "react-dom/client";
|
|
4
4
|
import "@/app/globals.css";
|
|
5
|
+
import { THEME_PRESETS } from "../shared/theme-presets";
|
|
6
|
+
import { getThemeFromUrl, mergeThemeProps } from "../shared/theme-utils";
|
|
5
7
|
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
// Try to import FeaturedCarousel if it exists
|
|
9
|
+
let FeaturedCarousel: React.ComponentType<any> | null = null;
|
|
10
|
+
try {
|
|
11
|
+
FeaturedCarousel = require("@/app/featured-carousel").default;
|
|
12
|
+
} catch (e) {
|
|
13
|
+
// featured-carousel.tsx doesn't exist, that's ok
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Declare global window property for theme application
|
|
17
|
+
declare global {
|
|
18
|
+
interface Window {
|
|
19
|
+
__linkapp_applyTheme?: (variables: Record<string, string>) => void
|
|
20
|
+
}
|
|
21
|
+
}
|
|
13
22
|
|
|
14
23
|
// Preview props injected by dev server via Vite define
|
|
15
24
|
declare const __PREVIEW_PROPS__: Record<string, unknown>;
|
|
16
25
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
},
|
|
38
|
-
locale: "en-US",
|
|
39
|
-
currency: "USD",
|
|
40
|
-
username: "demo-user",
|
|
41
|
-
viewport: {
|
|
42
|
-
width: 680,
|
|
43
|
-
height: 800,
|
|
44
|
-
},
|
|
45
|
-
};
|
|
26
|
+
// Extract just the variables from THEME_PRESETS for theme lookups
|
|
27
|
+
const THEME_VARS = Object.fromEntries(
|
|
28
|
+
Object.entries(THEME_PRESETS).map(([key, { variables }]) => [key, variables])
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Get theme variables and groupLayoutOption from URL
|
|
32
|
+
const params = new URLSearchParams(window.location.search);
|
|
33
|
+
const groupLayoutOptionParam = params.get('groupLayoutOption');
|
|
34
|
+
const themeVariables = getThemeFromUrl(THEME_VARS);
|
|
35
|
+
|
|
36
|
+
// Merge with preview props and add groupLayoutOption
|
|
37
|
+
const previewProps = __PREVIEW_PROPS__ || {};
|
|
38
|
+
const mergedProps = mergeThemeProps(themeVariables, previewProps, {
|
|
39
|
+
groupLayoutOption: groupLayoutOptionParam || undefined,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Apply theme CSS variables on mount
|
|
43
|
+
if (themeVariables && window.__linkapp_applyTheme) {
|
|
44
|
+
window.__linkapp_applyTheme(themeVariables);
|
|
45
|
+
}
|
|
46
46
|
|
|
47
47
|
const rootElement = document.getElementById("root");
|
|
48
48
|
if (!rootElement) {
|
|
49
49
|
throw new Error("Root element not found");
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// Select the appropriate component based on groupLayoutOption parameter
|
|
53
|
+
const LayoutComponent = (groupLayoutOptionParam === 'carousel' && FeaturedCarousel)
|
|
54
|
+
? FeaturedCarousel
|
|
55
|
+
: Featured;
|
|
56
|
+
|
|
57
|
+
console.log('[Featured Dev Server] Rendering:', {
|
|
58
|
+
groupLayoutOption: groupLayoutOptionParam,
|
|
59
|
+
component: groupLayoutOptionParam === 'carousel' && FeaturedCarousel ? 'FeaturedCarousel' : 'Featured',
|
|
60
|
+
hasFeaturedCarousel: !!FeaturedCarousel,
|
|
61
|
+
});
|
|
62
|
+
|
|
52
63
|
createRoot(rootElement).render(
|
|
53
64
|
<StrictMode>
|
|
54
|
-
{
|
|
55
|
-
<Layout theme={previewProps.theme}>
|
|
56
|
-
<Featured {...previewProps} />
|
|
57
|
-
</Layout>
|
|
58
|
-
) : (
|
|
59
|
-
<Featured {...previewProps} />
|
|
60
|
-
)}
|
|
65
|
+
<LayoutComponent {...mergedProps} />
|
|
61
66
|
</StrictMode>,
|
|
62
67
|
);
|
package/dev-server/featured.html
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Featured - LinkApp</title>
|
|
7
7
|
|
|
8
|
+
<!-- iframe-resizer: Required for iframe height auto-adjustment -->
|
|
9
|
+
<script crossorigin src="https://unpkg.com/iframe-resizer@4.3.2/js/iframeResizer.contentWindow.js"></script>
|
|
10
|
+
|
|
8
11
|
<!-- Linktree theme variables will be injected here -->
|
|
9
12
|
<style id="linktree-theme"></style>
|
|
10
13
|
|
|
@@ -21,46 +24,29 @@
|
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
27
|
+
* Apply theme CSS variables to :root
|
|
28
|
+
* Called once on mount from main.tsx
|
|
29
|
+
* @param {Record<string, string>} variables - CSS variables to apply
|
|
26
30
|
*/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
typeof event.data === 'object' &&
|
|
32
|
-
event.data.type === 'THEME_UPDATE'
|
|
33
|
-
) {
|
|
34
|
-
const themeUpdate = event.data
|
|
35
|
-
|
|
36
|
-
// Get the style element where theme variables are injected
|
|
37
|
-
const themeStyle = document.getElementById('linktree-theme')
|
|
31
|
+
function applyTheme(variables) {
|
|
32
|
+
if (!variables || typeof variables !== 'object') {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return
|
|
42
|
-
}
|
|
36
|
+
// Get the style element where theme variables are injected
|
|
37
|
+
const themeStyle = document.getElementById('linktree-theme')
|
|
43
38
|
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
if (!themeStyle) {
|
|
40
|
+
console.warn('linktree-theme style element not found')
|
|
41
|
+
return
|
|
42
|
+
}
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (className.startsWith('linktree-theme-')) {
|
|
51
|
-
classesToRemove.push(className)
|
|
52
|
-
}
|
|
53
|
-
})
|
|
54
|
-
classesToRemove.forEach((className) => {
|
|
55
|
-
document.documentElement.classList.remove(className)
|
|
56
|
-
})
|
|
44
|
+
// Inject CSS variables into :root
|
|
45
|
+
themeStyle.textContent = ':root { ' + renderCssVariables(variables) + ' }'
|
|
46
|
+
}
|
|
57
47
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
document.documentElement.classList.add('linktree-theme-' + themeUpdate.payload.name)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}, false)
|
|
48
|
+
// Export applyTheme to be used by main.tsx
|
|
49
|
+
window.__linkapp_applyTheme = applyTheme
|
|
64
50
|
</script>
|
|
65
51
|
</head>
|
|
66
52
|
<body>
|
package/dev-server/index.html
CHANGED
|
@@ -5,8 +5,15 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Preview - LinkApp</title>
|
|
7
7
|
|
|
8
|
+
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
|
|
9
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
10
|
+
<link rel="shortcut icon" href="/favicon.ico" />
|
|
11
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
|
12
|
+
<meta name="apple-mobile-web-app-title" content="Linktree" />
|
|
13
|
+
<link rel="manifest" href="/site.webmanifest" />
|
|
14
|
+
|
|
8
15
|
<!-- iframe-resizer: Required for LinkApps to communicate with Linktree parent frame -->
|
|
9
|
-
<script crossorigin src="https://unpkg.com/iframe-resizer@4.3.2/js/iframeResizer.contentWindow.
|
|
16
|
+
<script crossorigin src="https://unpkg.com/iframe-resizer@4.3.2/js/iframeResizer.contentWindow.js"></script>
|
|
10
17
|
</head>
|
|
11
18
|
<body>
|
|
12
19
|
<div id="root"></div>
|
package/dev-server/lib/utils.ts
CHANGED
|
@@ -4,3 +4,9 @@ import { twMerge } from 'tailwind-merge'
|
|
|
4
4
|
export function cn(...inputs: ClassValue[]) {
|
|
5
5
|
return twMerge(clsx(inputs))
|
|
6
6
|
}
|
|
7
|
+
|
|
8
|
+
export const renderCssVariables = (vars: Record<string, string>): string => {
|
|
9
|
+
return Object.entries(vars)
|
|
10
|
+
.map((entry) => entry.join(': '))
|
|
11
|
+
.join('; ') + ';'
|
|
12
|
+
}
|