@linktr.ee/linkapp 0.0.22 → 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.
Files changed (76) hide show
  1. package/dev-server/README.md +5 -5
  2. package/dev-server/{vite-env.d.ts → env.d.ts} +1 -1
  3. package/dev-server/featured/main.tsx +32 -311
  4. package/dev-server/package.json +5 -6
  5. package/dev-server/postcss/tailwind-source-fallback.js +94 -0
  6. package/dev-server/postcss.config.mjs +28 -3
  7. package/dev-server/preview/Preview.tsx +114 -339
  8. package/dev-server/preview/preview.css +3 -0
  9. package/dev-server/rsbuild.config.ts +45 -0
  10. package/dev-server/shared/theme-presets.ts +315 -0
  11. package/dev-server/shared/theme-utils.ts +38 -0
  12. package/dev-server/sheet/main.tsx +44 -0
  13. package/dev-server/{classic.html → sheet.html} +2 -2
  14. package/dist/commands/add.d.ts.map +1 -1
  15. package/dist/commands/add.js +20 -12
  16. package/dist/commands/add.js.map +1 -1
  17. package/dist/commands/build.d.ts.map +1 -1
  18. package/dist/commands/build.js +55 -36
  19. package/dist/commands/build.js.map +1 -1
  20. package/dist/commands/deploy.d.ts.map +1 -1
  21. package/dist/commands/deploy.js +58 -45
  22. package/dist/commands/deploy.js.map +1 -1
  23. package/dist/commands/dev.d.ts.map +1 -1
  24. package/dist/commands/dev.js +541 -128
  25. package/dist/commands/dev.js.map +1 -1
  26. package/dist/commands/login.d.ts.map +1 -1
  27. package/dist/commands/login.js +19 -9
  28. package/dist/commands/login.js.map +1 -1
  29. package/dist/commands/logout.d.ts.map +1 -1
  30. package/dist/commands/logout.js +9 -4
  31. package/dist/commands/logout.js.map +1 -1
  32. package/dist/commands/test-url-match-rules.d.ts.map +1 -1
  33. package/dist/commands/test-url-match-rules.js +24 -13
  34. package/dist/commands/test-url-match-rules.js.map +1 -1
  35. package/dist/lib/build/detect-layouts.d.ts +1 -1
  36. package/dist/lib/build/detect-layouts.d.ts.map +1 -1
  37. package/dist/lib/build/detect-layouts.js +8 -7
  38. package/dist/lib/build/detect-layouts.js.map +1 -1
  39. package/dist/lib/deploy/pack-project.js +1 -1
  40. package/dist/lib/deploy/pack-project.js.map +1 -1
  41. package/dist/lib/deploy/validation.d.ts.map +1 -1
  42. package/dist/lib/deploy/validation.js +8 -5
  43. package/dist/lib/deploy/validation.js.map +1 -1
  44. package/dist/lib/rsbuild/config-factory.d.ts +24 -0
  45. package/dist/lib/rsbuild/config-factory.d.ts.map +1 -0
  46. package/dist/lib/rsbuild/config-factory.js +135 -0
  47. package/dist/lib/rsbuild/config-factory.js.map +1 -0
  48. package/dist/lib/rsbuild/plugins/asset-versioning.d.ts +11 -0
  49. package/dist/lib/rsbuild/plugins/asset-versioning.d.ts.map +1 -0
  50. package/dist/lib/rsbuild/plugins/asset-versioning.js +62 -0
  51. package/dist/lib/rsbuild/plugins/asset-versioning.js.map +1 -0
  52. package/dist/lib/rsbuild/plugins/copy-public.d.ts +11 -0
  53. package/dist/lib/rsbuild/plugins/copy-public.d.ts.map +1 -0
  54. package/dist/lib/rsbuild/plugins/copy-public.js +32 -0
  55. package/dist/lib/rsbuild/plugins/copy-public.js.map +1 -0
  56. package/dist/lib/rsbuild/postcss/tailwind-source-fallback.d.ts +12 -0
  57. package/dist/lib/rsbuild/postcss/tailwind-source-fallback.d.ts.map +1 -0
  58. package/dist/lib/rsbuild/postcss/tailwind-source-fallback.js +60 -0
  59. package/dist/lib/rsbuild/postcss/tailwind-source-fallback.js.map +1 -0
  60. package/dist/lib/utils/setup-runtime.d.ts.map +1 -1
  61. package/dist/lib/utils/setup-runtime.js +60 -26
  62. package/dist/lib/utils/setup-runtime.js.map +1 -1
  63. package/dist/lib/vite/config-factory.d.ts.map +1 -1
  64. package/dist/lib/vite/config-factory.js +5 -1
  65. package/dist/lib/vite/config-factory.js.map +1 -1
  66. package/dist/lib/vite/plugins/copy-public.d.ts +12 -0
  67. package/dist/lib/vite/plugins/copy-public.d.ts.map +1 -0
  68. package/dist/lib/vite/plugins/copy-public.js +31 -0
  69. package/dist/lib/vite/plugins/copy-public.js.map +1 -0
  70. package/dist/schema/config.schema.d.ts +18 -1
  71. package/dist/schema/config.schema.d.ts.map +1 -1
  72. package/dist/schema/config.schema.js +1 -0
  73. package/dist/schema/config.schema.js.map +1 -1
  74. package/package.json +7 -3
  75. package/dev-server/classic/main.tsx +0 -346
  76. package/dev-server/vite.config.ts +0 -34
@@ -15,337 +15,30 @@ import {
15
15
  } from "../components/ui/tabs";
16
16
  import { cn, renderCssVariables } from "../lib/utils";
17
17
  import { SettingsPreview } from "../components/SettingsPreview";
18
-
19
- // Theme presets for testing
20
- // Based on actual CSS variables from live Linktree profiles
21
- const THEME_PRESETS = {
22
- default: {
23
- name: "Default (Light)",
24
- variables: {
25
- "--button-style-text": "#000000",
26
- "--button-style-background": "#ffffff",
27
- "--button-style-background-hover":
28
- "color-mix(in srgb, #ffffff 93%, #000000 7%)",
29
- "--button-style-border": "none",
30
- "--button-style-border-color": "transparent",
31
- "--button-style-shadow": "none",
32
- "--button-style-shadow-color": "#000000",
33
- "--button-style-contrast-color": "#000000",
34
- "--button-style-radius": "8px",
35
- "--button-style-inner-radius": "min(8px, max(4px, calc(8px - 8px)))",
36
- "--button-style-skeleton-color": "rgba(0, 0, 0, 0.05)",
37
- "--link-gap": "14px",
38
- "--link-inner-padding": "7px",
39
- "--link-preview-thumbnail-width": "160px",
40
- "--linkRadius": "8px",
41
- "--profile-container-desktop-width": "580px",
42
- "--profileBackground": "#eceef1",
43
- "--background-contrast-color": "#000000",
44
- "--bodyText": "#000000",
45
- "--profileTitleText": "#000000",
46
- "--profileDescriptionText": "#000000",
47
- "--defaultAvatarBackground": "#000000",
48
- "--defaultAvatarText": "#ffffff",
49
- "--socialLinkFill": "#000000",
50
- "--linkBackground": "#ffffff",
51
- "--linkText": "#000000",
52
- "--linkHoverBackground": "color-mix(in srgb, #ffffff 93%, #000000 7%)",
53
- "--linkHoverText": "#000000",
54
- "--linkShadow": "#000000",
55
- "--bannerBackground": "#ffffff",
56
- "--bannerText": "#000000",
57
- "--desktop-frame-color": "color-mix(in srgb, #eceef1 88%, black 12%)",
58
- "--profileFontFamilyPrimary":
59
- 'Link Sans Product, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
60
- "--profileFontFamilySecondary":
61
- 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
62
- "--header-font-family":
63
- 'Link Sans Product, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
64
- "--profileFontWeightNormal": "500",
65
- "--profileFontWeightBold": "700",
66
- "--profileDescriptionFontWeight": "500",
67
- "--linkTextFontWeight": "500",
68
- "--headerFontWeight": "700",
69
- "--header-font-weight": "600",
70
- "--embedLinkTextFontWeight": "500",
71
- "--signupSubmitTextFontWeight": "700",
72
- "--bannerFontWeight": "700",
73
- "--headerFontSize": "normal",
74
- "--header-bio-font-size": "16px",
75
- "--embedLinkTextFontSize": "14px",
76
- "--signupSubmitTextFontSize": "14px",
77
- "--linkHeaderFontSize": "14px",
78
- "--bannerFontSize": "14px",
79
- "--profileDescriptionLineHeight": "1.5",
80
- "--headerLineHeight": "1.5",
81
- "--header-text-color": "#000000",
82
- },
83
- },
84
- dark: {
85
- name: "Dark",
86
- variables: {
87
- "--button-style-text": "#ffffff",
88
- "--button-style-background": "#1f2937",
89
- "--button-style-background-hover":
90
- "color-mix(in srgb, #1f2937 93%, #ffffff 7%)",
91
- "--button-style-border": "none",
92
- "--button-style-border-color": "transparent",
93
- "--button-style-shadow": "none",
94
- "--button-style-shadow-color": "#000000",
95
- "--button-style-contrast-color": "#ffffff",
96
- "--button-style-radius": "8px",
97
- "--button-style-inner-radius": "min(8px, max(4px, calc(8px - 8px)))",
98
- "--button-style-skeleton-color": "rgba(255, 255, 255, 0.1)",
99
- "--link-gap": "14px",
100
- "--link-inner-padding": "7px",
101
- "--link-preview-thumbnail-width": "160px",
102
- "--linkRadius": "8px",
103
- "--profile-container-desktop-width": "580px",
104
- "--profileBackground": "#111827",
105
- "--background-contrast-color": "#ffffff",
106
- "--bodyText": "#ffffff",
107
- "--profileTitleText": "#ffffff",
108
- "--profileDescriptionText": "#ffffff",
109
- "--defaultAvatarBackground": "#ffffff",
110
- "--defaultAvatarText": "#000000",
111
- "--socialLinkFill": "#ffffff",
112
- "--linkBackground": "#1f2937",
113
- "--linkText": "#ffffff",
114
- "--linkHoverBackground": "color-mix(in srgb, #1f2937 93%, #ffffff 7%)",
115
- "--linkHoverText": "#ffffff",
116
- "--linkShadow": "#000000",
117
- "--bannerBackground": "#1f2937",
118
- "--bannerText": "#ffffff",
119
- "--desktop-frame-color": "color-mix(in srgb, #111827 88%, white 12%)",
120
- "--profileFontFamilyPrimary":
121
- 'Link Sans Product, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
122
- "--profileFontFamilySecondary":
123
- 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
124
- "--header-font-family":
125
- 'Link Sans Product, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
126
- "--profileFontWeightNormal": "500",
127
- "--profileFontWeightBold": "700",
128
- "--profileDescriptionFontWeight": "500",
129
- "--linkTextFontWeight": "500",
130
- "--headerFontWeight": "700",
131
- "--header-font-weight": "600",
132
- "--embedLinkTextFontWeight": "500",
133
- "--signupSubmitTextFontWeight": "700",
134
- "--bannerFontWeight": "700",
135
- "--headerFontSize": "normal",
136
- "--header-bio-font-size": "16px",
137
- "--embedLinkTextFontSize": "14px",
138
- "--signupSubmitTextFontSize": "14px",
139
- "--linkHeaderFontSize": "14px",
140
- "--bannerFontSize": "14px",
141
- "--profileDescriptionLineHeight": "1.5",
142
- "--headerLineHeight": "1.5",
143
- "--header-text-color": "#ffffff",
144
- },
145
- },
146
- purple: {
147
- name: "Purple",
148
- variables: {
149
- "--button-style-text": "#ffffff",
150
- "--button-style-background": "#7c3aed",
151
- "--button-style-background-hover":
152
- "color-mix(in srgb, #7c3aed 93%, #ffffff 7%)",
153
- "--button-style-border": "none",
154
- "--button-style-border-color": "transparent",
155
- "--button-style-shadow": "none",
156
- "--button-style-shadow-color": "#000000",
157
- "--button-style-contrast-color": "#ede9fe",
158
- "--button-style-radius": "16px",
159
- "--button-style-inner-radius": "min(16px, max(4px, calc(16px - 8px)))",
160
- "--button-style-skeleton-color": "rgba(124, 58, 237, 0.1)",
161
- "--link-gap": "14px",
162
- "--link-inner-padding": "7px",
163
- "--link-preview-thumbnail-width": "160px",
164
- "--linkRadius": "16px",
165
- "--profile-container-desktop-width": "580px",
166
- "--profileBackground": "#faf5ff",
167
- "--background-contrast-color": "#1f2937",
168
- "--bodyText": "#1f2937",
169
- "--profileTitleText": "#1f2937",
170
- "--profileDescriptionText": "#1f2937",
171
- "--defaultAvatarBackground": "#7c3aed",
172
- "--defaultAvatarText": "#ffffff",
173
- "--socialLinkFill": "#1f2937",
174
- "--linkBackground": "#7c3aed",
175
- "--linkText": "#ffffff",
176
- "--linkHoverBackground": "color-mix(in srgb, #7c3aed 93%, #ffffff 7%)",
177
- "--linkHoverText": "#ffffff",
178
- "--linkShadow": "#7c3aed",
179
- "--bannerBackground": "#7c3aed",
180
- "--bannerText": "#ffffff",
181
- "--desktop-frame-color": "color-mix(in srgb, #faf5ff 88%, black 12%)",
182
- "--profileFontFamilyPrimary":
183
- 'Link Sans Product, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
184
- "--profileFontFamilySecondary":
185
- 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
186
- "--header-font-family":
187
- 'Link Sans Product, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
188
- "--profileFontWeightNormal": "500",
189
- "--profileFontWeightBold": "700",
190
- "--profileDescriptionFontWeight": "500",
191
- "--linkTextFontWeight": "500",
192
- "--headerFontWeight": "700",
193
- "--header-font-weight": "600",
194
- "--embedLinkTextFontWeight": "500",
195
- "--signupSubmitTextFontWeight": "700",
196
- "--bannerFontWeight": "700",
197
- "--headerFontSize": "normal",
198
- "--header-bio-font-size": "16px",
199
- "--embedLinkTextFontSize": "14px",
200
- "--signupSubmitTextFontSize": "14px",
201
- "--linkHeaderFontSize": "14px",
202
- "--bannerFontSize": "14px",
203
- "--profileDescriptionLineHeight": "1.5",
204
- "--headerLineHeight": "1.5",
205
- "--header-text-color": "#1f2937",
206
- },
207
- },
208
- outline: {
209
- name: "Outline Style",
210
- variables: {
211
- "--button-style-text": "#000000",
212
- "--button-style-background": "transparent",
213
- "--button-style-background-hover": "rgba(0, 0, 0, 0.05)",
214
- "--button-style-border": "2px solid currentColor",
215
- "--button-style-border-color": "#000000",
216
- "--button-style-shadow": "none",
217
- "--button-style-shadow-color": "#000000",
218
- "--button-style-contrast-color": "#ffffff",
219
- "--button-style-radius": "24px",
220
- "--button-style-inner-radius": "min(24px, max(4px, calc(24px - 8px)))",
221
- "--button-style-skeleton-color": "rgba(0, 0, 0, 0.05)",
222
- "--link-gap": "14px",
223
- "--link-inner-padding": "7px",
224
- "--link-preview-thumbnail-width": "160px",
225
- "--linkRadius": "24px",
226
- "--profile-container-desktop-width": "580px",
227
- "--profileBackground": "#ffffff",
228
- "--background-contrast-color": "#000000",
229
- "--bodyText": "#000000",
230
- "--profileTitleText": "#000000",
231
- "--profileDescriptionText": "#000000",
232
- "--defaultAvatarBackground": "#000000",
233
- "--defaultAvatarText": "#ffffff",
234
- "--socialLinkFill": "#000000",
235
- "--linkBackground": "transparent",
236
- "--linkText": "#000000",
237
- "--linkHoverBackground": "rgba(0, 0, 0, 0.05)",
238
- "--linkHoverText": "#000000",
239
- "--linkShadow": "#000000",
240
- "--bannerBackground": "#ffffff",
241
- "--bannerText": "#000000",
242
- "--desktop-frame-color": "color-mix(in srgb, #ffffff 88%, black 12%)",
243
- "--profileFontFamilyPrimary":
244
- 'Link Sans Product, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
245
- "--profileFontFamilySecondary":
246
- 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
247
- "--header-font-family":
248
- 'Link Sans Product, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
249
- "--profileFontWeightNormal": "500",
250
- "--profileFontWeightBold": "700",
251
- "--profileDescriptionFontWeight": "500",
252
- "--linkTextFontWeight": "500",
253
- "--headerFontWeight": "700",
254
- "--header-font-weight": "600",
255
- "--embedLinkTextFontWeight": "500",
256
- "--signupSubmitTextFontWeight": "700",
257
- "--bannerFontWeight": "700",
258
- "--headerFontSize": "normal",
259
- "--header-bio-font-size": "16px",
260
- "--embedLinkTextFontSize": "14px",
261
- "--signupSubmitTextFontSize": "14px",
262
- "--linkHeaderFontSize": "14px",
263
- "--bannerFontSize": "14px",
264
- "--profileDescriptionLineHeight": "1.5",
265
- "--headerLineHeight": "1.5",
266
- "--header-text-color": "#000000",
267
- },
268
- },
269
- rounded: {
270
- name: "Fully Rounded",
271
- variables: {
272
- "--button-style-text": "#ffffff",
273
- "--button-style-background": "#059669",
274
- "--button-style-background-hover":
275
- "color-mix(in srgb, #059669 93%, #ffffff 7%)",
276
- "--button-style-border": "none",
277
- "--button-style-border-color": "transparent",
278
- "--button-style-shadow": "none",
279
- "--button-style-shadow-color": "#000000",
280
- "--button-style-contrast-color": "#d1fae5",
281
- "--button-style-radius": "28px",
282
- "--button-style-inner-radius": "28px",
283
- "--button-style-skeleton-color": "rgba(5, 150, 105, 0.1)",
284
- "--link-gap": "14px",
285
- "--link-inner-padding": "7px",
286
- "--link-preview-thumbnail-width": "160px",
287
- "--linkRadius": "28px",
288
- "--profile-container-desktop-width": "580px",
289
- "--profileBackground": "#ecfdf5",
290
- "--background-contrast-color": "#1f2937",
291
- "--bodyText": "#1f2937",
292
- "--profileTitleText": "#1f2937",
293
- "--profileDescriptionText": "#1f2937",
294
- "--defaultAvatarBackground": "#059669",
295
- "--defaultAvatarText": "#ffffff",
296
- "--socialLinkFill": "#1f2937",
297
- "--linkBackground": "#059669",
298
- "--linkText": "#ffffff",
299
- "--linkHoverBackground": "color-mix(in srgb, #059669 93%, #ffffff 7%)",
300
- "--linkHoverText": "#ffffff",
301
- "--linkShadow": "#059669",
302
- "--bannerBackground": "#059669",
303
- "--bannerText": "#ffffff",
304
- "--desktop-frame-color": "color-mix(in srgb, #ecfdf5 88%, black 12%)",
305
- "--profileFontFamilyPrimary":
306
- 'Link Sans Product, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
307
- "--profileFontFamilySecondary":
308
- 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
309
- "--header-font-family":
310
- 'Link Sans Product, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
311
- "--profileFontWeightNormal": "500",
312
- "--profileFontWeightBold": "700",
313
- "--profileDescriptionFontWeight": "500",
314
- "--linkTextFontWeight": "500",
315
- "--headerFontWeight": "700",
316
- "--header-font-weight": "600",
317
- "--embedLinkTextFontWeight": "500",
318
- "--signupSubmitTextFontWeight": "700",
319
- "--bannerFontWeight": "700",
320
- "--headerFontSize": "normal",
321
- "--header-bio-font-size": "16px",
322
- "--embedLinkTextFontSize": "14px",
323
- "--signupSubmitTextFontSize": "14px",
324
- "--linkHeaderFontSize": "14px",
325
- "--bannerFontSize": "14px",
326
- "--profileDescriptionLineHeight": "1.5",
327
- "--headerLineHeight": "1.5",
328
- "--header-text-color": "#1f2937",
329
- },
330
- },
331
- };
332
-
333
- let iframeCount = 0;
18
+ import { THEME_PRESETS } from "../shared/theme-presets";
334
19
 
335
20
  export default function Preview() {
336
21
  // Initialize state from localStorage
337
- const [selectedTab, setSelectedTab] = useState<"classic" | "featured" | "settings">(() => {
22
+ const [selectedTab, setSelectedTab] = useState<"sheet" | "featured" | "settings">(() => {
338
23
  const saved = localStorage.getItem("linkapp-preview-tab");
339
- return (saved as "classic" | "featured" | "settings") || "classic";
24
+ return (saved as "sheet" | "featured" | "settings") || "sheet";
340
25
  });
341
26
  const [selectedTheme, setSelectedTheme] = useState<keyof typeof THEME_PRESETS>(() => {
342
27
  const saved = localStorage.getItem("linkapp-preview-theme");
343
28
  return (saved as keyof typeof THEME_PRESETS) || "default";
344
29
  });
30
+ const [selectedGroupLayoutOption, setSelectedGroupLayoutOption] = useState<string | undefined>(() => {
31
+ const saved = localStorage.getItem("linkapp-preview-groupLayoutOption");
32
+ return saved || undefined;
33
+ });
34
+
35
+ // Popup dialog state
36
+ const [isPopupOpen, setIsPopupOpen] = useState(false);
345
37
 
346
- // Generate unique IDs for iframes
347
- const classicIframeId = useMemo(() => `preview-iframe-classic-${iframeCount++}`, []);
348
- const featuredIframeId = useMemo(() => `preview-iframe-featured-${iframeCount++}`, []);
38
+ // Generate unique IDs for iframes using timestamp
39
+ const sheetIframeId = useMemo(() => `preview-iframe-sheet-${Date.now()}`, []);
40
+ const featuredIframeId = useMemo(() => `preview-iframe-featured-${Date.now()}`, []);
41
+ const popupIframeId = useMemo(() => `preview-iframe-popup-${Date.now()}`, []);
349
42
 
350
43
  // Save selected tab to localStorage
351
44
  useEffect(() => {
@@ -357,18 +50,42 @@ export default function Preview() {
357
50
  localStorage.setItem("linkapp-preview-theme", selectedTheme);
358
51
  }, [selectedTheme]);
359
52
 
53
+ // Save selected groupLayoutOption to localStorage
54
+ useEffect(() => {
55
+ if (selectedGroupLayoutOption) {
56
+ localStorage.setItem("linkapp-preview-groupLayoutOption", selectedGroupLayoutOption);
57
+ } else {
58
+ localStorage.removeItem("linkapp-preview-groupLayoutOption");
59
+ }
60
+ }, [selectedGroupLayoutOption]);
61
+
360
62
  // Handle iframe resize events
361
63
  const handleResized = useCallback(
362
64
  (ev: { iframe: IFrameComponent; height: number; width: number; type: string }): void => {
363
65
  // IframeResizer automatically handles height adjustments
364
- // This callback is mainly for debugging or additional logic if needed
365
- if (!ev.height) {
366
- console.warn('Preview iframe reported zero height');
367
- }
66
+ // This callback is available for debugging if needed
368
67
  },
369
68
  []
370
69
  );
371
70
 
71
+ // Handle postMessage from featured iframe for OPEN_POPUP
72
+ const handleMessage = useCallback((event: MessageEvent) => {
73
+ if (
74
+ event.data &&
75
+ typeof event.data === 'object' &&
76
+ event.data.source === 'linkapp' &&
77
+ event.data.type === 'OPEN_POPUP'
78
+ ) {
79
+ setIsPopupOpen(true);
80
+ }
81
+ }, []);
82
+
83
+ // Add message listener
84
+ useEffect(() => {
85
+ window.addEventListener('message', handleMessage);
86
+ return () => window.removeEventListener('message', handleMessage);
87
+ }, [handleMessage]);
88
+
372
89
 
373
90
  const renderedCssVariables = useMemo(() => {
374
91
  const themeVariables = THEME_PRESETS[selectedTheme] || THEME_PRESETS.default
@@ -391,7 +108,7 @@ export default function Preview() {
391
108
  <Tabs
392
109
  value={selectedTab}
393
110
  onValueChange={(value) =>
394
- setSelectedTab(value as "classic" | "featured" | "settings")
111
+ setSelectedTab(value as "sheet" | "featured" | "settings")
395
112
  }
396
113
  >
397
114
  <Portal>
@@ -400,7 +117,7 @@ export default function Preview() {
400
117
  style={{ zIndex: 1000000 }}
401
118
  >
402
119
  <TabsList>
403
- <TabsTrigger value="classic">Classic</TabsTrigger>
120
+ <TabsTrigger value="sheet">Sheet</TabsTrigger>
404
121
  <TabsTrigger value="featured">Featured</TabsTrigger>
405
122
  <TabsTrigger value="settings">Settings</TabsTrigger>
406
123
  </TabsList>
@@ -425,26 +142,46 @@ export default function Preview() {
425
142
  ))}
426
143
  </select>
427
144
  </div>
145
+
146
+ {/* Group Layout Option Switcher - Only show for Featured tab */}
147
+ {selectedTab === "featured" && (
148
+ <div className="flex items-center gap-2">
149
+ <label htmlFor="groupLayoutOption-select" className="text-sm font-medium">
150
+ Layout:
151
+ </label>
152
+ <select
153
+ id="groupLayoutOption-select"
154
+ value={selectedGroupLayoutOption || "default"}
155
+ onChange={(e) =>
156
+ setSelectedGroupLayoutOption(e.target.value === "default" ? undefined : e.target.value)
157
+ }
158
+ className="h-9 px-3 py-1 text-sm border border-input bg-background rounded-md focus:outline-none focus:ring-2 focus:ring-ring"
159
+ >
160
+ <option value="default">Default</option>
161
+ <option value="carousel">Carousel</option>
162
+ </select>
163
+ </div>
164
+ )}
428
165
  </div>
429
166
  </Portal>
430
167
 
431
168
  {/* Main Content with Padding for Fixed Tabs */}
432
169
  <div className="pt-20">
433
- <TabsContent value="classic" className="m-0">
434
- {/* Classic Modal - Always Open */}
170
+ <TabsContent value="sheet" className="m-0">
171
+ {/* Sheet Modal - Always Open */}
435
172
  <Dialog open={true} modal={false}>
436
173
  <DialogContent
437
174
  className="h-[calc(100dvh-2rem)] max-w-[608px] md:min-h-[25vh] md:h-[80%] md:max-h-[900px] overflow-auto"
438
175
  showCloseButton={false}
439
176
  >
440
177
  <DialogHeader className="sticky top-0 bg-white px-4">
441
- <div className="grid h-[64px] grid-cols-[32px_auto_32px] items-center gap-4">
178
+ <div className="grid h-16 grid-cols-[32px_auto_32px] items-center gap-4">
442
179
  <button className="flex size-8 items-center justify-center rounded-sm focus-visible:outline-none">
443
180
  <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className=" " role="img" aria-hidden="true"><path fill="currentColor" d="m10.65 3.85.35.36.7-.71-.35-.35-3-3h-.7l-3 3-.36.35.71.7.35-.35L7.5 1.71V10h1V1.7l2.15 2.15ZM1 5.5l.5-.5H4v1H2v9h12V6h-2V5h2.5l.5.5v10l-.5.5h-13l-.5-.5v-10Z"></path></svg>
444
181
  </button>
445
182
 
446
183
  <DialogTitle className="self-center truncate py-3 text-center">
447
- Classic
184
+ Sheet
448
185
  </DialogTitle>
449
186
 
450
187
  <button className="flex size-8 items-center justify-center rounded-sm focus-visible:outline-none">
@@ -456,10 +193,10 @@ export default function Preview() {
456
193
  <div className="flex h-[calc(100%-64px)] flex-1 flex-col justify-between overflow-hidden">
457
194
  <div className="h-full overflow-y-auto overflow-x-hidden">
458
195
  <IframeResizer
459
- key={`classic-${selectedTheme}`}
460
- id={classicIframeId}
461
- src={`/classic?theme=${selectedTheme}`}
462
- style={{ height: '0px', width: '1px', minWidth: '100%', border: 0, borderRadius: '0.5rem' }}
196
+ key={`sheet-${selectedTheme}`}
197
+ id={sheetIframeId}
198
+ src={`/sheet?theme=${selectedTheme}`}
199
+ style={{ height: '0px', width: '1px', minWidth: '100%', border: 0 }}
463
200
  checkOrigin={false}
464
201
  onResized={handleResized}
465
202
  heightCalculationMethod="max"
@@ -475,12 +212,12 @@ export default function Preview() {
475
212
  <div className="w-full max-w-[580px] flex flex-col px-[28px] py-7 items-center bg-linktree-profile-bg">
476
213
 
477
214
  <div className="w-full max-w-[524px]">
478
- <div className="bg-linktree-button-bg hover:bg-linktree-button-bg-hover border-linktree-button-border text-linktree-button-text rounded-linktree-button shadow-linktree-button">
215
+ <div className="bg-linktree-button-bg hover:bg-linktree-button-bg-hover border-linktree-button-border text-linktree-button-text rounded-linktree-button shadow-linktree-button overflow-hidden">
479
216
  <IframeResizer
480
- key={`featured-${selectedTheme}`}
217
+ key={`featured-${selectedTheme}-${selectedGroupLayoutOption || 'default'}`}
481
218
  id={featuredIframeId}
482
- src={`/featured?theme=${selectedTheme}`}
483
- style={{ height: '0px', width: '1px', minWidth: '100%', border: 0, borderRadius: '0.5rem' }}
219
+ src={`/featured?theme=${selectedTheme}${selectedGroupLayoutOption ? `&groupLayoutOption=${selectedGroupLayoutOption}` : ''}`}
220
+ style={{ height: '0px', width: '1px', minWidth: '100%', border: 0 }}
484
221
  checkOrigin={false}
485
222
  onResized={handleResized}
486
223
  heightCalculationMethod="max"
@@ -495,6 +232,44 @@ export default function Preview() {
495
232
  </TabsContent>
496
233
  </div>
497
234
  </Tabs>
235
+
236
+ {/* Popup Dialog for OPEN_POPUP message */}
237
+ <Dialog open={isPopupOpen} onOpenChange={setIsPopupOpen} modal={false}>
238
+ <DialogContent
239
+ className="h-[calc(100dvh-2rem)] max-w-[608px] md:min-h-[25vh] md:h-[80%] md:max-h-[900px] overflow-auto"
240
+ showCloseButton={false}
241
+ >
242
+ <DialogHeader className="sticky top-0 bg-white px-4">
243
+ <div className="grid h-16 grid-cols-[32px_auto_32px] items-center gap-4">
244
+ <button className="flex size-8 items-center justify-center rounded-sm focus-visible:outline-none">
245
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className=" " role="img" aria-hidden="true"><path fill="currentColor" d="m10.65 3.85.35.36.7-.71-.35-.35-3-3h-.7l-3 3-.36.35.71.7.35-.35L7.5 1.71V10h1V1.7l2.15 2.15ZM1 5.5l.5-.5H4v1H2v9h12V6h-2V5h2.5l.5.5v10l-.5.5h-13l-.5-.5v-10Z"></path></svg>
246
+ </button>
247
+
248
+ <DialogTitle className="self-center truncate py-3 text-center">
249
+ Sheet
250
+ </DialogTitle>
251
+
252
+ <button className="flex size-8 items-center justify-center rounded-sm focus-visible:outline-none">
253
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className=" " role="img" aria-hidden="true"><path fill="currentColor" d="m13.63 3.12.37-.38-.74-.74-.38.37.75.75ZM2.37 12.89l-.37.37.74.74.38-.37-.75-.75Zm.75-10.52L2.74 2 2 2.74l.37.38.75-.75Zm9.76 11.26.38.37.74-.74-.37-.38-.75.75Zm0-11.26L2.38 12.9l.74.74 10.5-10.51-.74-.75Zm-10.5.75 10.5 10.5.75-.73L3.12 2.37l-.75.75Z"></path></svg>
254
+ </button>
255
+ </div>
256
+ </DialogHeader>
257
+
258
+ <div className="flex h-[calc(100%-64px)] flex-1 flex-col justify-between overflow-hidden">
259
+ <div className="h-full overflow-y-auto overflow-x-hidden">
260
+ <IframeResizer
261
+ key={`popup-${selectedTheme}`}
262
+ id={popupIframeId}
263
+ src={`/sheet?theme=${selectedTheme}`}
264
+ style={{ height: '0px', width: '1px', minWidth: '100%', border: 0 }}
265
+ checkOrigin={false}
266
+ onResized={handleResized}
267
+ heightCalculationMethod="max"
268
+ />
269
+ </div>
270
+ </div>
271
+ </DialogContent>
272
+ </Dialog>
498
273
  </div>
499
274
  </>
500
275
  );
@@ -1,6 +1,9 @@
1
1
  @import "tailwindcss";
2
2
  @import "tw-animate-css";
3
3
 
4
+ @source "../components";
5
+ @source "../preview";
6
+
4
7
  @theme inline {
5
8
  --color-background: var(--background);
6
9
  --color-foreground: var(--foreground);
@@ -0,0 +1,45 @@
1
+ import { defineConfig } from '@rsbuild/core'
2
+ import { pluginReact } from '@rsbuild/plugin-react'
3
+ import { resolve } from 'path'
4
+
5
+ export default defineConfig({
6
+ plugins: [pluginReact()],
7
+ source: {
8
+ // Define fallback values - these will be overridden by dev.ts
9
+ define: {
10
+ __PREVIEW_PROPS__: 'undefined',
11
+ __SETTINGS_CONFIG__: 'undefined',
12
+ },
13
+ },
14
+ resolve: {
15
+ alias: {
16
+ '@': resolve(__dirname, '..'),
17
+ '@/components': resolve(__dirname, 'components'),
18
+ '@/lib': resolve(__dirname, 'lib'),
19
+ },
20
+ },
21
+ html: {
22
+ template: resolve(__dirname, 'index.html'),
23
+ },
24
+ server: {
25
+ port: 3000,
26
+ strictPort: true,
27
+ },
28
+ tools: {
29
+ postcss: (opts) => {
30
+ // Support Tailwind CSS v4 via PostCSS
31
+ opts.postcssOptions = opts.postcssOptions || {}
32
+ opts.postcssOptions.plugins = opts.postcssOptions.plugins || []
33
+
34
+ // Add Tailwind CSS PostCSS plugin
35
+ try {
36
+ const tailwindPostcss = require('@tailwindcss/postcss')
37
+ opts.postcssOptions.plugins.push(tailwindPostcss())
38
+ } catch {
39
+ // @tailwindcss/postcss not installed
40
+ }
41
+
42
+ return opts
43
+ },
44
+ },
45
+ })