@linktr.ee/linkapp 0.0.47 → 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.
@@ -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, renderCssVariables } from "../lib/utils";
18
+ import { cn } from "../lib/utils";
19
19
  import { THEME_PRESETS } from "../shared/theme-presets";
20
20
 
21
21
  function Chin({ title }: { title?: string }) {
@@ -134,18 +134,13 @@ export default function Preview() {
134
134
  return () => window.removeEventListener("message", handleMessage);
135
135
  }, [handleMessage]);
136
136
 
137
- const renderedCssVariables = useMemo(() => {
138
- const themeVariables =
139
- THEME_PRESETS[selectedTheme] || THEME_PRESETS.default;
140
- return renderCssVariables(themeVariables.variables);
137
+ const themeVariables = useMemo(() => {
138
+ const theme = THEME_PRESETS[selectedTheme] || THEME_PRESETS.default;
139
+ return theme.variables;
141
140
  }, [selectedTheme]);
142
141
 
143
142
  return (
144
143
  <>
145
- <style>{`:root {
146
- ${renderedCssVariables}
147
- }`}</style>
148
-
149
144
  <div
150
145
  className={cn("min-h-screen", {
151
146
  "bg-black/50": selectedTab === "expanded",
@@ -154,18 +149,19 @@ export default function Preview() {
154
149
  })}
155
150
  id="preview"
156
151
  >
157
- <Tabs
158
- value={selectedTab}
159
- onValueChange={(value) =>
160
- setSelectedTab(
161
- (value === "sheet" ? "expanded" : value) as
162
- | "expanded"
163
- | "featured"
164
- | "carousel"
165
- | "settings",
166
- )
167
- }
168
- >
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
+ >
169
165
  <Portal>
170
166
  <div
171
167
  className="fixed top-0 left-0 right-0 p-4 flex justify-center gap-4 bg-background border-b"
@@ -390,6 +386,7 @@ export default function Preview() {
390
386
  </TabsContent>
391
387
  </div>
392
388
  </Tabs>
389
+ </div>
393
390
 
394
391
  {/* Popup Dialog for EXPAND_LINK_APP message */}
395
392
  <Dialog open={isPopupOpen} onOpenChange={setIsPopupOpen}>
@@ -1 +1 @@
1
- {"version":3,"file":"setup-runtime.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/setup-runtime.ts"],"names":[],"mappings":"AAmQA;;;GAGG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAyBtD"}
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"}
@@ -165,15 +165,19 @@ ${renderLogic}
165
165
  }
166
166
 
167
167
  const postExtensionReadyMessage = () => {
168
- const height = rootElement.clientHeight
169
- const message = {
170
- type: 'extension-ready',
171
- data: {
172
- ready: true,
173
- height: height,
174
- },
175
- }
176
- window.parent.postMessage(message, '*')
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
+ })
177
181
  }
178
182
 
179
183
  type RootInstance = ReturnType<typeof createRoot>
@@ -238,7 +242,7 @@ if (isInIframe) {
238
242
  { type: 'interaction-event', data: customEvent.detail },
239
243
  '*'
240
244
  )
241
- })
245
+ }, { passive: true })
242
246
  } else {
243
247
  // Development mode: render immediately with mock data
244
248
  renderApp(mockContext)
@@ -1 +1 @@
1
- {"version":3,"file":"setup-runtime.js","sourceRoot":"","sources":["../../../src/lib/utils/setup-runtime.ts"],"names":[],"mappings":"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"}
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.47",
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",
@@ -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