@linktr.ee/linkapp 0.0.46 → 0.0.48
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/components/ui/field.tsx +62 -0
- package/dev-server/components/ui/input.tsx +25 -0
- package/dev-server/components/ui/label.tsx +21 -0
- package/dev-server/components/ui/textarea.tsx +23 -0
- package/dev-server/preview/Preview.tsx +260 -263
- package/dist/lib/utils/setup-runtime.d.ts.map +1 -1
- package/dist/lib/utils/setup-runtime.js +26 -21
- package/dist/lib/utils/setup-runtime.js.map +1 -1
- package/package.json +6 -1
- package/runtime/index.html +28 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../lib/utils"
|
|
4
|
+
import { Label } from "./label"
|
|
5
|
+
|
|
6
|
+
interface FieldProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
orientation?: "vertical" | "horizontal"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const Field = React.forwardRef<HTMLDivElement, FieldProps>(
|
|
11
|
+
({ className, orientation = "vertical", ...props }, ref) => (
|
|
12
|
+
<div
|
|
13
|
+
ref={ref}
|
|
14
|
+
role="group"
|
|
15
|
+
className={cn(
|
|
16
|
+
"space-y-2",
|
|
17
|
+
orientation === "horizontal" && "flex items-start gap-3 space-y-0",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
Field.displayName = "Field"
|
|
25
|
+
|
|
26
|
+
const FieldLabel = React.forwardRef<
|
|
27
|
+
React.ElementRef<typeof Label>,
|
|
28
|
+
React.ComponentPropsWithoutRef<typeof Label>
|
|
29
|
+
>(({ className, ...props }, ref) => (
|
|
30
|
+
<Label
|
|
31
|
+
ref={ref}
|
|
32
|
+
className={cn("text-sm font-medium text-gray-900", className)}
|
|
33
|
+
{...props}
|
|
34
|
+
/>
|
|
35
|
+
))
|
|
36
|
+
FieldLabel.displayName = "FieldLabel"
|
|
37
|
+
|
|
38
|
+
const FieldDescription = React.forwardRef<
|
|
39
|
+
HTMLParagraphElement,
|
|
40
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
41
|
+
>(({ className, ...props }, ref) => (
|
|
42
|
+
<p
|
|
43
|
+
ref={ref}
|
|
44
|
+
className={cn("text-sm text-gray-500", className)}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
))
|
|
48
|
+
FieldDescription.displayName = "FieldDescription"
|
|
49
|
+
|
|
50
|
+
const FieldError = React.forwardRef<
|
|
51
|
+
HTMLParagraphElement,
|
|
52
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
53
|
+
>(({ className, ...props }, ref) => (
|
|
54
|
+
<p
|
|
55
|
+
ref={ref}
|
|
56
|
+
className={cn("text-sm text-red-500", className)}
|
|
57
|
+
{...props}
|
|
58
|
+
/>
|
|
59
|
+
))
|
|
60
|
+
FieldError.displayName = "FieldError"
|
|
61
|
+
|
|
62
|
+
export { Field, FieldLabel, FieldDescription, FieldError }
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../lib/utils"
|
|
4
|
+
|
|
5
|
+
const Input = React.forwardRef<
|
|
6
|
+
HTMLInputElement,
|
|
7
|
+
React.InputHTMLAttributes<HTMLInputElement>
|
|
8
|
+
>(({ className, type, ...props }, ref) => (
|
|
9
|
+
<input
|
|
10
|
+
type={type}
|
|
11
|
+
className={cn(
|
|
12
|
+
"flex h-9 w-full rounded-md border border-gray-300 bg-white px-3 py-1 text-sm shadow-sm transition-colors",
|
|
13
|
+
"file:border-0 file:bg-transparent file:text-sm file:font-medium",
|
|
14
|
+
"placeholder:text-gray-400",
|
|
15
|
+
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-blue-500 focus-visible:border-blue-500",
|
|
16
|
+
"disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500",
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
ref={ref}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
))
|
|
23
|
+
Input.displayName = "Input"
|
|
24
|
+
|
|
25
|
+
export { Input }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as LabelPrimitive from "@radix-ui/react-label"
|
|
3
|
+
|
|
4
|
+
import { cn } from "../../lib/utils"
|
|
5
|
+
|
|
6
|
+
const Label = React.forwardRef<
|
|
7
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
8
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
|
9
|
+
>(({ className, ...props }, ref) => (
|
|
10
|
+
<LabelPrimitive.Root
|
|
11
|
+
ref={ref}
|
|
12
|
+
className={cn(
|
|
13
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
14
|
+
className
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
))
|
|
19
|
+
Label.displayName = LabelPrimitive.Root.displayName
|
|
20
|
+
|
|
21
|
+
export { Label }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../lib/utils"
|
|
4
|
+
|
|
5
|
+
const Textarea = React.forwardRef<
|
|
6
|
+
HTMLTextAreaElement,
|
|
7
|
+
React.TextareaHTMLAttributes<HTMLTextAreaElement>
|
|
8
|
+
>(({ className, ...props }, ref) => (
|
|
9
|
+
<textarea
|
|
10
|
+
className={cn(
|
|
11
|
+
"flex min-h-[80px] w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm shadow-sm transition-colors",
|
|
12
|
+
"placeholder:text-gray-400",
|
|
13
|
+
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-blue-500 focus-visible:border-blue-500",
|
|
14
|
+
"disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500 disabled:resize-none",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
ref={ref}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
))
|
|
21
|
+
Textarea.displayName = "Textarea"
|
|
22
|
+
|
|
23
|
+
export { Textarea }
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
TabsList,
|
|
16
16
|
TabsTrigger,
|
|
17
17
|
} from "../components/ui/tabs";
|
|
18
|
-
import { cn
|
|
18
|
+
import { cn } from "../lib/utils";
|
|
19
19
|
import { THEME_PRESETS } from "../shared/theme-presets";
|
|
20
20
|
|
|
21
21
|
function Chin({ title }: { title?: string }) {
|
|
@@ -36,28 +36,31 @@ function Chin({ title }: { title?: string }) {
|
|
|
36
36
|
|
|
37
37
|
export default function Preview() {
|
|
38
38
|
// These are injected by the dev server
|
|
39
|
-
// @ts-expect-error - injected by dev server
|
|
40
|
-
const hasFeatured =
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
// @ts-expect-error - injected by dev server
|
|
40
|
+
const hasFeatured =
|
|
41
|
+
typeof __HAS_FEATURED__ !== "undefined" && __HAS_FEATURED__;
|
|
42
|
+
// @ts-expect-error - injected by dev server
|
|
43
|
+
const hasCarousel =
|
|
44
|
+
typeof __HAS_CAROUSEL__ !== "undefined" && __HAS_CAROUSEL__;
|
|
44
45
|
|
|
45
|
-
// Initialize state from localStorage
|
|
46
|
+
// Initialize state from localStorage
|
|
46
47
|
const [selectedTab, setSelectedTab] = useState<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
48
|
+
"expanded" | "featured" | "carousel" | "settings"
|
|
49
|
+
>(() => {
|
|
50
|
+
const saved = localStorage.getItem("linkapp-preview-tab");
|
|
51
|
+
if (saved === "featured" && !hasFeatured) {
|
|
52
|
+
return "expanded";
|
|
53
|
+
}
|
|
54
|
+
if (saved === "carousel" && !hasCarousel) {
|
|
55
|
+
return "expanded";
|
|
56
|
+
}
|
|
57
|
+
if (saved === "sheet") {
|
|
58
|
+
return "expanded";
|
|
59
|
+
}
|
|
60
|
+
return (
|
|
61
|
+
(saved as "expanded" | "featured" | "carousel" | "settings") || "expanded"
|
|
62
|
+
);
|
|
63
|
+
});
|
|
61
64
|
const [selectedTheme, setSelectedTheme] = useState<
|
|
62
65
|
keyof typeof THEME_PRESETS
|
|
63
66
|
>(() => {
|
|
@@ -131,50 +134,49 @@ const hasCarousel =
|
|
|
131
134
|
return () => window.removeEventListener("message", handleMessage);
|
|
132
135
|
}, [handleMessage]);
|
|
133
136
|
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
return renderCssVariables(themeVariables.variables);
|
|
137
|
+
const themeVariables = useMemo(() => {
|
|
138
|
+
const theme = THEME_PRESETS[selectedTheme] || THEME_PRESETS.default;
|
|
139
|
+
return theme.variables;
|
|
138
140
|
}, [selectedTheme]);
|
|
139
141
|
|
|
140
142
|
return (
|
|
141
143
|
<>
|
|
142
|
-
<style>{`:root {
|
|
143
|
-
${renderedCssVariables}
|
|
144
|
-
}`}</style>
|
|
145
|
-
|
|
146
144
|
<div
|
|
147
145
|
className={cn("min-h-screen", {
|
|
148
146
|
"bg-black/50": selectedTab === "expanded",
|
|
149
|
-
"bg-linktree-frame":
|
|
147
|
+
"bg-linktree-frame":
|
|
148
|
+
selectedTab === "featured" || selectedTab === "carousel",
|
|
150
149
|
})}
|
|
151
150
|
id="preview"
|
|
152
151
|
>
|
|
153
|
-
<
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
152
|
+
<div style={themeVariables}>
|
|
153
|
+
<Tabs
|
|
154
|
+
value={selectedTab}
|
|
155
|
+
onValueChange={(value) =>
|
|
156
|
+
setSelectedTab(
|
|
157
|
+
(value === "sheet" ? "expanded" : value) as
|
|
158
|
+
| "expanded"
|
|
159
|
+
| "featured"
|
|
160
|
+
| "carousel"
|
|
161
|
+
| "settings",
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
>
|
|
165
165
|
<Portal>
|
|
166
166
|
<div
|
|
167
167
|
className="fixed top-0 left-0 right-0 p-4 flex justify-center gap-4 bg-background border-b"
|
|
168
168
|
style={{ zIndex: 1000000 }}
|
|
169
169
|
>
|
|
170
170
|
<TabsList>
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
171
|
+
<TabsTrigger value="expanded">Expanded</TabsTrigger>
|
|
172
|
+
{hasFeatured && (
|
|
173
|
+
<TabsTrigger value="featured">Featured</TabsTrigger>
|
|
174
|
+
)}
|
|
175
|
+
{hasCarousel && (
|
|
176
|
+
<TabsTrigger value="carousel">Carousel</TabsTrigger>
|
|
177
|
+
)}
|
|
178
|
+
<TabsTrigger value="settings">Settings</TabsTrigger>
|
|
179
|
+
</TabsList>
|
|
178
180
|
|
|
179
181
|
{/* Theme Switcher */}
|
|
180
182
|
<div className="flex items-center gap-2">
|
|
@@ -206,7 +208,6 @@ const hasCarousel =
|
|
|
206
208
|
<TabsContent value="expanded" className="m-0">
|
|
207
209
|
{/* Expanded Modal - Always Open */}
|
|
208
210
|
<Dialog open={true} modal={false}>
|
|
209
|
-
<DialogOverlay />
|
|
210
211
|
<DialogContent
|
|
211
212
|
className="h-[calc(100dvh-2rem)] max-w-[608px] md:min-h-[25vh] md:h-[80%] md:max-h-[900px] overflow-auto"
|
|
212
213
|
showCloseButton={false}
|
|
@@ -252,216 +253,212 @@ const hasCarousel =
|
|
|
252
253
|
></path>
|
|
253
254
|
</svg>
|
|
254
255
|
</button>
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
</div>
|
|
465
|
-
</>
|
|
466
|
-
);
|
|
467
|
-
}
|
|
256
|
+
</div>
|
|
257
|
+
</DialogHeader>
|
|
258
|
+
|
|
259
|
+
<div className="flex h-[calc(100%-64px)] flex-1 flex-col justify-between overflow-hidden">
|
|
260
|
+
<div className="h-full overflow-y-auto overflow-x-hidden">
|
|
261
|
+
<IframeResizer
|
|
262
|
+
key={`expanded-${selectedTheme}`}
|
|
263
|
+
id={expandedIframeId}
|
|
264
|
+
src={`/expanded?theme=${selectedTheme}`}
|
|
265
|
+
style={{
|
|
266
|
+
height: "0px",
|
|
267
|
+
width: "1px",
|
|
268
|
+
minWidth: "100%",
|
|
269
|
+
border: 0,
|
|
270
|
+
}}
|
|
271
|
+
checkOrigin={false}
|
|
272
|
+
onResized={handleResized}
|
|
273
|
+
heightCalculationMethod="max"
|
|
274
|
+
/>
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
</DialogContent>
|
|
278
|
+
</Dialog>
|
|
279
|
+
</TabsContent>
|
|
280
|
+
|
|
281
|
+
{hasFeatured && (
|
|
282
|
+
<TabsContent
|
|
283
|
+
value="featured"
|
|
284
|
+
className="m-0 flex justify-center p-8"
|
|
285
|
+
>
|
|
286
|
+
<div className="w-full max-w-[580px] flex flex-col px-[28px] py-7 items-center bg-gray-100">
|
|
287
|
+
<div className="w-full max-w-[524px]">
|
|
288
|
+
{chinPosition === "above" && <Chin title={chinTitle} />}
|
|
289
|
+
|
|
290
|
+
<div
|
|
291
|
+
className={cn(
|
|
292
|
+
"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",
|
|
293
|
+
isOverlay && "relative",
|
|
294
|
+
)}
|
|
295
|
+
>
|
|
296
|
+
{chinPosition === "overlay_above" && (
|
|
297
|
+
<div className="absolute top-0 left-0 right-0 z-10">
|
|
298
|
+
<Chin title={chinTitle} />
|
|
299
|
+
</div>
|
|
300
|
+
)}
|
|
301
|
+
|
|
302
|
+
<IframeResizer
|
|
303
|
+
key={`featured-${selectedTheme}`}
|
|
304
|
+
id={featuredIframeId}
|
|
305
|
+
src={`/featured?theme=${selectedTheme}`}
|
|
306
|
+
style={{
|
|
307
|
+
height: "0px",
|
|
308
|
+
width: "1px",
|
|
309
|
+
minWidth: "100%",
|
|
310
|
+
border: 0,
|
|
311
|
+
}}
|
|
312
|
+
checkOrigin={false}
|
|
313
|
+
onResized={handleResized}
|
|
314
|
+
heightCalculationMethod="max"
|
|
315
|
+
/>
|
|
316
|
+
|
|
317
|
+
{chinPosition === "overlayBelow" && (
|
|
318
|
+
<div className="absolute bottom-0 left-0 right-0 z-10">
|
|
319
|
+
<Chin title={chinTitle} />
|
|
320
|
+
</div>
|
|
321
|
+
)}
|
|
322
|
+
</div>
|
|
323
|
+
|
|
324
|
+
{chinPosition === "below" && <Chin title={chinTitle} />}
|
|
325
|
+
</div>
|
|
326
|
+
</div>
|
|
327
|
+
</TabsContent>
|
|
328
|
+
)}
|
|
329
|
+
|
|
330
|
+
{hasCarousel && (
|
|
331
|
+
<TabsContent
|
|
332
|
+
value="carousel"
|
|
333
|
+
className="m-0 flex justify-center p-8"
|
|
334
|
+
>
|
|
335
|
+
<div className="w-full max-w-[580px] flex flex-col px-[28px] py-7 items-center bg-gray-100">
|
|
336
|
+
<div className="w-full max-w-[524px]">
|
|
337
|
+
{chinPosition === "above" && <Chin title={chinTitle} />}
|
|
338
|
+
|
|
339
|
+
<div
|
|
340
|
+
className={cn(
|
|
341
|
+
"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",
|
|
342
|
+
isOverlay && "relative",
|
|
343
|
+
)}
|
|
344
|
+
style={
|
|
345
|
+
!__SETTINGS_CONFIG__?.featured_head_allow_unlocked_aspect_ratio
|
|
346
|
+
? { aspectRatio: "11 / 8" }
|
|
347
|
+
: undefined
|
|
348
|
+
}
|
|
349
|
+
>
|
|
350
|
+
{chinPosition === "overlay_above" && (
|
|
351
|
+
<div className="absolute top-0 left-0 right-0 z-10">
|
|
352
|
+
<Chin title={chinTitle} />
|
|
353
|
+
</div>
|
|
354
|
+
)}
|
|
355
|
+
|
|
356
|
+
<IframeResizer
|
|
357
|
+
key={`carousel-${selectedTheme}`}
|
|
358
|
+
id={carouselIframeId}
|
|
359
|
+
src={`/featured-carousel?theme=${selectedTheme}`}
|
|
360
|
+
style={{
|
|
361
|
+
height: "0px",
|
|
362
|
+
width: "1px",
|
|
363
|
+
minWidth: "100%",
|
|
364
|
+
border: 0,
|
|
365
|
+
}}
|
|
366
|
+
checkOrigin={false}
|
|
367
|
+
onResized={handleResized}
|
|
368
|
+
heightCalculationMethod="max"
|
|
369
|
+
/>
|
|
370
|
+
|
|
371
|
+
{chinPosition === "overlayBelow" && (
|
|
372
|
+
<div className="absolute bottom-0 left-0 right-0 z-10">
|
|
373
|
+
<Chin title={chinTitle} />
|
|
374
|
+
</div>
|
|
375
|
+
)}
|
|
376
|
+
</div>
|
|
377
|
+
|
|
378
|
+
{chinPosition === "below" && <Chin title={chinTitle} />}
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
</TabsContent>
|
|
382
|
+
)}
|
|
383
|
+
|
|
384
|
+
<TabsContent value="settings" className="m-0">
|
|
385
|
+
<SettingsPreview settings={__SETTINGS_CONFIG__} />
|
|
386
|
+
</TabsContent>
|
|
387
|
+
</div>
|
|
388
|
+
</Tabs>
|
|
389
|
+
</div>
|
|
390
|
+
|
|
391
|
+
{/* Popup Dialog for EXPAND_LINK_APP message */}
|
|
392
|
+
<Dialog open={isPopupOpen} onOpenChange={setIsPopupOpen}>
|
|
393
|
+
<DialogContent
|
|
394
|
+
className="h-[calc(100dvh-2rem)] max-w-[608px] md:min-h-[25vh] md:h-[80%] md:max-h-[900px] overflow-auto"
|
|
395
|
+
showCloseButton={false}
|
|
396
|
+
>
|
|
397
|
+
<DialogHeader className="sticky top-0 bg-white px-4">
|
|
398
|
+
<div className="grid h-16 grid-cols-[32px_auto_32px] items-center gap-4">
|
|
399
|
+
<button className="flex size-8 items-center justify-center rounded-sm focus-visible:outline-none">
|
|
400
|
+
<svg
|
|
401
|
+
width="16"
|
|
402
|
+
height="16"
|
|
403
|
+
viewBox="0 0 16 16"
|
|
404
|
+
fill="none"
|
|
405
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
406
|
+
className=" "
|
|
407
|
+
role="img"
|
|
408
|
+
aria-hidden="true"
|
|
409
|
+
>
|
|
410
|
+
<path
|
|
411
|
+
fill="currentColor"
|
|
412
|
+
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"
|
|
413
|
+
></path>
|
|
414
|
+
</svg>
|
|
415
|
+
</button>
|
|
416
|
+
|
|
417
|
+
<DialogTitle className="self-center truncate py-3 text-center">
|
|
418
|
+
Expanded
|
|
419
|
+
</DialogTitle>
|
|
420
|
+
|
|
421
|
+
<button className="flex size-8 items-center justify-center rounded-sm focus-visible:outline-none">
|
|
422
|
+
<svg
|
|
423
|
+
width="16"
|
|
424
|
+
height="16"
|
|
425
|
+
viewBox="0 0 16 16"
|
|
426
|
+
fill="none"
|
|
427
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
428
|
+
className=" "
|
|
429
|
+
role="img"
|
|
430
|
+
aria-hidden="true"
|
|
431
|
+
>
|
|
432
|
+
<path
|
|
433
|
+
fill="currentColor"
|
|
434
|
+
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"
|
|
435
|
+
></path>
|
|
436
|
+
</svg>
|
|
437
|
+
</button>
|
|
438
|
+
</div>
|
|
439
|
+
</DialogHeader>
|
|
440
|
+
|
|
441
|
+
<div className="flex h-[calc(100%-64px)] flex-1 flex-col justify-between overflow-hidden">
|
|
442
|
+
<div className="h-full overflow-y-auto overflow-x-hidden">
|
|
443
|
+
<IframeResizer
|
|
444
|
+
key={`popup-${selectedTheme}`}
|
|
445
|
+
id={popupIframeId}
|
|
446
|
+
src={`/expanded?theme=${selectedTheme}`}
|
|
447
|
+
style={{
|
|
448
|
+
height: "0px",
|
|
449
|
+
width: "1px",
|
|
450
|
+
minWidth: "100%",
|
|
451
|
+
border: 0,
|
|
452
|
+
}}
|
|
453
|
+
checkOrigin={false}
|
|
454
|
+
onResized={handleResized}
|
|
455
|
+
heightCalculationMethod="max"
|
|
456
|
+
/>
|
|
457
|
+
</div>
|
|
458
|
+
</div>
|
|
459
|
+
</DialogContent>
|
|
460
|
+
</Dialog>
|
|
461
|
+
</div>
|
|
462
|
+
</>
|
|
463
|
+
);
|
|
464
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup-runtime.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/setup-runtime.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"setup-runtime.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/setup-runtime.ts"],"names":[],"mappings":"AAuQA;;;GAGG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAyBtD"}
|
|
@@ -125,25 +125,26 @@ function App({ data }: { data: ExtensionData }) {
|
|
|
125
125
|
const baseLayout = data.__layout || params.get('layout') || 'classic'
|
|
126
126
|
const groupLayoutOption = data.groupLayoutOption || params.get('groupLayoutOption')
|
|
127
127
|
|
|
128
|
-
// Simple layout selection
|
|
129
|
-
let
|
|
128
|
+
// Simple layout selection with context-aware fallbacks
|
|
129
|
+
let LayoutComponent
|
|
130
130
|
|
|
131
|
-
if (baseLayout === '
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
if (baseLayout === 'featured' && groupLayoutOption === 'carousel') {
|
|
132
|
+
// Featured carousel: try carousel → featured → expanded → sheet
|
|
133
|
+
LayoutComponent = layouts['featured-carousel'] || layouts.featured || layouts.expanded || layouts.sheet
|
|
134
|
+
} else if (baseLayout === 'featured') {
|
|
135
|
+
// Featured: try featured → expanded → sheet
|
|
136
|
+
LayoutComponent = layouts.featured || layouts.expanded || layouts.sheet
|
|
135
137
|
} else {
|
|
136
|
-
|
|
138
|
+
// Classic/expanded: try expanded → sheet
|
|
139
|
+
LayoutComponent = layouts.expanded || layouts.sheet
|
|
137
140
|
}
|
|
138
141
|
|
|
139
|
-
const LayoutComponent = layouts[layoutName] || layouts.expanded || layouts.sheet
|
|
140
|
-
|
|
141
142
|
// Debug logging (only in development or when component is missing)
|
|
142
143
|
if (!isInIframe || !LayoutComponent) {
|
|
143
144
|
console.log('[LinkApp] Layout selection:', {
|
|
144
145
|
__layout: data.__layout,
|
|
145
146
|
groupLayoutOption: data.groupLayoutOption,
|
|
146
|
-
|
|
147
|
+
baseLayout,
|
|
147
148
|
availableLayouts: Object.keys(layouts),
|
|
148
149
|
})
|
|
149
150
|
}
|
|
@@ -152,7 +153,7 @@ function App({ data }: { data: ExtensionData }) {
|
|
|
152
153
|
return (
|
|
153
154
|
<div style={{ padding: '20px', fontFamily: 'system-ui' }}>
|
|
154
155
|
<h1>Error: Layout not found</h1>
|
|
155
|
-
<p>
|
|
156
|
+
<p>No suitable layout component could be found.</p>
|
|
156
157
|
<p>Available layouts: {Object.keys(layouts).join(', ')}</p>
|
|
157
158
|
</div>
|
|
158
159
|
)
|
|
@@ -164,15 +165,19 @@ ${renderLogic}
|
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
const postExtensionReadyMessage = () => {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
168
|
+
// Use requestAnimationFrame to batch layout reads with browser paint cycle
|
|
169
|
+
// This prevents forced synchronous layout and improves scroll performance
|
|
170
|
+
requestAnimationFrame(() => {
|
|
171
|
+
const height = rootElement.clientHeight
|
|
172
|
+
const message = {
|
|
173
|
+
type: 'extension-ready',
|
|
174
|
+
data: {
|
|
175
|
+
ready: true,
|
|
176
|
+
height: height,
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
window.parent.postMessage(message, '*')
|
|
180
|
+
})
|
|
176
181
|
}
|
|
177
182
|
|
|
178
183
|
type RootInstance = ReturnType<typeof createRoot>
|
|
@@ -237,7 +242,7 @@ if (isInIframe) {
|
|
|
237
242
|
{ type: 'interaction-event', data: customEvent.detail },
|
|
238
243
|
'*'
|
|
239
244
|
)
|
|
240
|
-
})
|
|
245
|
+
}, { passive: true })
|
|
241
246
|
} else {
|
|
242
247
|
// Development mode: render immediately with mock data
|
|
243
248
|
renderApp(mockContext)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup-runtime.js","sourceRoot":"","sources":["../../../src/lib/utils/setup-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE3D,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC;;GAEG;AACH,SAAS,eAAe,CAAC,WAAmB;IAC1C,MAAM,SAAS,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;IAClC,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC;IACtC,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;IAE5E,6CAA6C;IAC7C,MAAM,OAAO,GAAG,OAAO;SACpB,GAAG,CACF,CAAC,MAAM,EAAE,EAAE,CACT,UAAU,MAAM,CAAC,WAAW,iBAAiB,MAAM,CAAC,QAAQ,GAAG,CAClE;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,8EAA8E;IAC9E,MAAM,aAAa,GAAa,EAAE,CAAC;IACnC,IAAI,cAAc,EAAE,CAAC;QACnB,aAAa,CAAC,IAAI,CAAC,YAAY,cAAc,CAAC,WAAW,GAAG,CAAC,CAAC;IAChE,CAAC;IAED,2CAA2C;IAC3C,MAAM,YAAY,GAAG,SAAS;QAC5B,CAAC,CAAC,wCAAwC;QAC1C,CAAC,CAAC,EAAE,CAAC;IAEP,0BAA0B;IAC1B,MAAM,UAAU,GAAG;QACjB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;YACxB,kCAAkC;YAClC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;YACzE,OAAO,KAAK,GAAG,KAAK,MAAM,CAAC,WAAW,GAAG,CAAC;QAC5C,CAAC,CAAC;QACF,GAAG,aAAa;KACjB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,kFAAkF;IAClF,MAAM,WAAW,GAAG,SAAS;QAC3B,CAAC,CAAC;;gBAEU;QACZ,CAAC,CAAC,qCAAqC,CAAC;IAE1C,OAAO;;EAEP,YAAY;EACZ,OAAO;;;;;EAKP,UAAU
|
|
1
|
+
{"version":3,"file":"setup-runtime.js","sourceRoot":"","sources":["../../../src/lib/utils/setup-runtime.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linktr.ee/linkapp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.48",
|
|
4
4
|
"description": "Development, build, and deployment tooling for LinkApps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -39,8 +39,13 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@clack/prompts": "^0.8.2",
|
|
42
|
+
"@radix-ui/react-checkbox": "^1.1.4",
|
|
42
43
|
"@radix-ui/react-dialog": "^1.1.3",
|
|
44
|
+
"@radix-ui/react-label": "^2.1.2",
|
|
43
45
|
"@radix-ui/react-portal": "^1.1.3",
|
|
46
|
+
"@radix-ui/react-radio-group": "^1.2.2",
|
|
47
|
+
"@radix-ui/react-select": "^2.1.6",
|
|
48
|
+
"@radix-ui/react-switch": "^1.1.2",
|
|
44
49
|
"@radix-ui/react-tabs": "^1.1.3",
|
|
45
50
|
"@rsbuild/core": "^1.6.6",
|
|
46
51
|
"@rsbuild/plugin-react": "^1.4.2",
|
package/runtime/index.html
CHANGED
|
@@ -26,6 +26,34 @@
|
|
|
26
26
|
}
|
|
27
27
|
</style>
|
|
28
28
|
|
|
29
|
+
<!-- iOS Safari scroll momentum fix -->
|
|
30
|
+
<!-- Prevents iframe from creating scroll context that interrupts parent momentum -->
|
|
31
|
+
<style id="ios-scroll-fix">
|
|
32
|
+
html {
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
height: 100%;
|
|
35
|
+
/* Force GPU compositor layer - reduces main thread sync */
|
|
36
|
+
transform: translateZ(0);
|
|
37
|
+
will-change: transform;
|
|
38
|
+
/* Contain layout to prevent parent reflows */
|
|
39
|
+
contain: layout style paint;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
body {
|
|
43
|
+
overflow: hidden;
|
|
44
|
+
height: 100%;
|
|
45
|
+
min-height: 100%;
|
|
46
|
+
/* Prevent overscroll behavior */
|
|
47
|
+
overscroll-behavior: none;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#root {
|
|
51
|
+
/* Allow taps but not scroll gestures */
|
|
52
|
+
touch-action: manipulation;
|
|
53
|
+
overflow: visible;
|
|
54
|
+
}
|
|
55
|
+
</style>
|
|
56
|
+
|
|
29
57
|
<script>
|
|
30
58
|
/**
|
|
31
59
|
* Helper function to convert CSS variables object to CSS string
|