@linktr.ee/create-link-app 1.9.0 → 2.0.0-rc.1

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.
@@ -28,33 +28,64 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  const react_1 = __importStar(require("react"));
30
30
  const client_1 = require("react-dom/client");
31
+ const simulator_1 = __importDefault(require("./simulator"));
31
32
  const extension_1 = __importDefault(require("@linktr.ee/extension"));
32
33
  const extension_dev_data_1 = __importDefault(require("@linktr.ee/extension-dev-data"));
33
34
  const container = document.getElementById('root');
34
35
  const root = (0, client_1.createRoot)(container);
35
- const App = () => {
36
- // TODO: refactor common postMessage to parent in both entry points
36
+ function EmbeddedExtension() {
37
+ const wrapperRef = (0, react_1.useRef)(null);
38
+ const [extensionData, setExtensionData] = (0, react_1.useState)(extension_dev_data_1.default);
37
39
  (0, react_1.useEffect)(() => {
38
- const message = {
39
- type: 'extension-ready',
40
- data: {
41
- ready: true,
42
- height: container.clientHeight,
43
- },
40
+ const postReady = (height) => {
41
+ const ready = { type: 'extension-ready', data: { ready: true, height } };
42
+ window.parent.postMessage(ready, '*');
43
+ };
44
+ // signal loaded first, then report initial size
45
+ const loaded = { type: 'extension-loaded', data: { loaded: true } };
46
+ window.parent.postMessage(loaded, '*');
47
+ const initialHeight = Math.ceil(wrapperRef.current?.getBoundingClientRect().height || document.documentElement.scrollHeight || container.clientHeight);
48
+ postReady(initialHeight);
49
+ const ro = new window.ResizeObserver((entries) => {
50
+ const entry = entries[0];
51
+ const h = Math.ceil(entry?.contentRect?.height || wrapperRef.current?.getBoundingClientRect().height || 0);
52
+ if (h)
53
+ postReady(h);
54
+ });
55
+ if (wrapperRef.current)
56
+ ro.observe(wrapperRef.current);
57
+ const onMessage = (event) => {
58
+ if (event.data?.type === 'extension-data') {
59
+ const data = event.data?.data || {};
60
+ // eslint-disable-next-line no-console
61
+ console.info('[Extension] received extension-data', data);
62
+ setExtensionData((prev) => ({ ...prev, ...data }));
63
+ }
64
+ };
65
+ // forward interaction events to the parent (parity with production)
66
+ const onInteraction = (event) => window.parent.postMessage({ type: 'interaction-event', data: event.detail }, '*');
67
+ document.addEventListener('interaction-event', onInteraction);
68
+ window.addEventListener('message', onMessage);
69
+ return () => {
70
+ if (wrapperRef.current) {
71
+ try {
72
+ ro.unobserve(wrapperRef.current);
73
+ }
74
+ catch (e) {
75
+ // ignore unobserve errors in dev simulator
76
+ }
77
+ }
78
+ document.removeEventListener('interaction-event', onInteraction);
79
+ window.removeEventListener('message', onMessage);
44
80
  };
45
- window.parent.postMessage(message, '*');
46
81
  }, []);
82
+ return (react_1.default.createElement("div", { ref: wrapperRef, style: { display: 'block' } },
83
+ react_1.default.createElement(extension_1.default, { ...extensionData })));
84
+ }
85
+ const App = () => {
47
86
  if (window.location.search === '?embed') {
48
- return react_1.default.createElement(extension_1.default, { ...extension_dev_data_1.default });
49
- }
50
- else {
51
- return (react_1.default.createElement("iframe", { src: `${window.location}?embed`, sandbox: ['allow-scripts', 'allow-same-origin', 'allow-popups', 'allow-popups-to-escape-sandbox', 'allow-forms'].join(' '), scrolling: "no", frameBorder: 0, style: {
52
- display: 'block',
53
- margin: '0 auto',
54
- width: '1px',
55
- minWidth: '680px',
56
- height: '100vh',
57
- } }));
87
+ return react_1.default.createElement(EmbeddedExtension, null);
58
88
  }
89
+ return react_1.default.createElement(simulator_1.default, null);
59
90
  };
60
91
  root.render(react_1.default.createElement(App, null));
@@ -0,0 +1,509 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const react_1 = __importStar(require("react"));
30
+ const styled_components_1 = __importStar(require("styled-components"));
31
+ const iframe_resizer_react_1 = __importDefault(require("iframe-resizer-react"));
32
+ const extension_dev_data_1 = __importDefault(require("@linktr.ee/extension-dev-data"));
33
+ function coerceExtensionData(input) {
34
+ if (input !== null && typeof input === 'object' && !Array.isArray(input)) {
35
+ return input;
36
+ }
37
+ return {};
38
+ }
39
+ const Global = (0, styled_components_1.createGlobalStyle) `
40
+ html, body, #root { height: 100%; }
41
+ body { margin: 0; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"; background: #111317; }
42
+ `;
43
+ const Page = styled_components_1.default.div `
44
+ min-height: 100vh;
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ gap: 24px;
49
+ padding: 24px;
50
+ box-sizing: border-box;
51
+ `;
52
+ // Simulated Linktree mobile device frame
53
+ const Phone = styled_components_1.default.div `
54
+ width: 394px; /* close to iPhone 14 width incl padding */
55
+ max-width: 94vw;
56
+ height: 800px;
57
+ border-radius: 28px;
58
+ background: radial-gradient(120% 110% at 50% 0%, #1f2143 0%, #2b2048 40%, #345a78 100%);
59
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.06);
60
+ color: #f5e8ff;
61
+ position: relative;
62
+ overflow: hidden;
63
+ &:before {
64
+ content: '';
65
+ position: absolute;
66
+ inset: -40% -20% 30% -20%;
67
+ background: radial-gradient(60% 40% at 50% 20%, rgba(214, 163, 255, 0.45) 0%, rgba(214, 163, 255, 0.18) 35%, rgba(0, 0, 0, 0) 80%);
68
+ pointer-events: none;
69
+ }
70
+ `;
71
+ const HeaderChrome = styled_components_1.default.div `
72
+ position: absolute;
73
+ top: 12px;
74
+ left: 0;
75
+ right: 0;
76
+ display: flex;
77
+ align-items: center;
78
+ justify-content: space-between;
79
+ padding: 0 18px;
80
+ `;
81
+ const Notch = styled_components_1.default.div `
82
+ position: absolute;
83
+ top: 10px;
84
+ left: 50%;
85
+ transform: translateX(-50%);
86
+ width: 120px;
87
+ height: 22px;
88
+ background: rgba(0, 0, 0, 0.42);
89
+ border-radius: 12px;
90
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15);
91
+ `;
92
+ const RoundIcon = styled_components_1.default.div `
93
+ width: 36px;
94
+ height: 36px;
95
+ border-radius: 9999px;
96
+ display: grid;
97
+ place-items: center;
98
+ background: rgba(255, 255, 255, 0.08);
99
+ color: #e8d7ff;
100
+ font-size: 16px;
101
+ backdrop-filter: blur(2px);
102
+ `;
103
+ const ScrollArea = styled_components_1.default.div `
104
+ position: absolute;
105
+ inset: 56px 0 0;
106
+ overflow-y: auto;
107
+ padding: 24px 22px 28px;
108
+ box-sizing: border-box;
109
+ `;
110
+ const SettingsPanel = styled_components_1.default.div `
111
+ width: 320px;
112
+ max-width: calc(100% - 24px);
113
+ background: rgba(255, 255, 255, 0.98);
114
+ color: #1f1147;
115
+ border-radius: 14px;
116
+ box-shadow: 0 10px 24px rgba(0, 0, 0, 0.25);
117
+ padding: 12px 14px;
118
+ border: 1px solid rgba(0, 0, 0, 0.05);
119
+ `;
120
+ const Profile = styled_components_1.default.div `
121
+ display: flex;
122
+ flex-direction: column;
123
+ align-items: center;
124
+ gap: 8px;
125
+ margin-bottom: 16px;
126
+ `;
127
+ const Avatar = styled_components_1.default.div `
128
+ width: 90px;
129
+ height: 90px;
130
+ border-radius: 9999px;
131
+ overflow: hidden;
132
+ border: 3px solid rgba(255, 255, 255, 0.25);
133
+ background: linear-gradient(135deg, #a5b4fc, #f0abfc);
134
+ display: grid;
135
+ place-items: center;
136
+ color: #1f1147;
137
+ font-weight: 800;
138
+ font-size: 28px;
139
+ `;
140
+ const Name = styled_components_1.default.div `
141
+ display: inline-flex;
142
+ align-items: center;
143
+ gap: 8px;
144
+ font-size: 28px;
145
+ font-weight: 700;
146
+ letter-spacing: 0.2px;
147
+ `;
148
+ const Badge = styled_components_1.default.span `
149
+ display: inline-flex;
150
+ width: 18px;
151
+ height: 18px;
152
+ border-radius: 50%;
153
+ background: #7dd3fc;
154
+ color: #0a2540;
155
+ font-size: 12px;
156
+ align-items: center;
157
+ justify-content: center;
158
+ `;
159
+ const Bio = styled_components_1.default.div `
160
+ font-size: 14px;
161
+ opacity: 0.9;
162
+ `;
163
+ const Card = styled_components_1.default.div `
164
+ background: rgba(255, 255, 255, 0.18);
165
+ color: #2a1c44;
166
+ border-radius: 20px;
167
+ padding: 14px 14px;
168
+ display: grid;
169
+ grid-template-columns: 36px 1fr 24px;
170
+ align-items: center;
171
+ gap: 12px;
172
+ margin: 12px 0;
173
+ backdrop-filter: blur(4px);
174
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 0 rgba(0, 0, 0, 0.15);
175
+ `;
176
+ const CardTitle = styled_components_1.default.div `
177
+ color: #2a1c44;
178
+ font-weight: 700;
179
+ font-size: 16px;
180
+ `;
181
+ const CardSub = styled_components_1.default.div `
182
+ color: #2a1c44;
183
+ opacity: 0.8;
184
+ font-size: 12px;
185
+ margin-top: 2px;
186
+ `;
187
+ const CardIcon = styled_components_1.default.div `
188
+ width: 36px;
189
+ height: 36px;
190
+ border-radius: 12px;
191
+ display: grid;
192
+ place-items: center;
193
+ background: rgba(255, 255, 255, 0.55);
194
+ color: #2a1c44;
195
+ font-size: 16px;
196
+ font-weight: 700;
197
+ `;
198
+ const Kebab = styled_components_1.default.div `
199
+ opacity: 0.8;
200
+ color: #2a1c44;
201
+ display: grid;
202
+ place-items: center;
203
+ font-size: 18px;
204
+ `;
205
+ const SocialRow = styled_components_1.default.div `
206
+ display: flex;
207
+ gap: 28px;
208
+ justify-content: center;
209
+ margin: 26px 0 12px;
210
+ opacity: 0.95;
211
+ `;
212
+ const Footer = styled_components_1.default.div `
213
+ text-align: center;
214
+ font-size: 12px;
215
+ opacity: 0.75;
216
+ color: #d8c9ec;
217
+ margin-top: 18px;
218
+ `;
219
+ // Dedicated container for extension embeds to better mirror production
220
+ const ExtensionCard = styled_components_1.default.div `
221
+ background: rgba(255, 255, 255, 0.95);
222
+ color: #0a0a0a;
223
+ border-radius: 20px;
224
+ padding: 0;
225
+ margin: 12px 0;
226
+ overflow: hidden;
227
+ position: relative;
228
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 0 rgba(0, 0, 0, 0.15);
229
+ `;
230
+ function kebab() {
231
+ return react_1.default.createElement("span", { "aria-hidden": true }, "\u22EE");
232
+ }
233
+ function Simulator() {
234
+ const [loading, setLoading] = (0, react_1.useState)(true);
235
+ const [loadFailed, setLoadFailed] = (0, react_1.useState)(false);
236
+ const [settingsOpen, setSettingsOpen] = (0, react_1.useState)(false);
237
+ // Allow overriding default dev data via window.postMessage({ type: 'extension-data', data: {...} })
238
+ const [extensionData] = (0, react_1.useState)(() => coerceExtensionData(extension_dev_data_1.default));
239
+ const DEFAULT_THEME = {
240
+ textColor: '#54417e',
241
+ backgroundColor: '#f2c2f3',
242
+ borderRadius: 'var(--button-style-inner-radius)',
243
+ borderColor: '#e9eaeb',
244
+ isOutlineStyle: false,
245
+ contrastColor: 'black',
246
+ textHoverColor: '#54417e',
247
+ buttonColor: '#7c3aed',
248
+ };
249
+ const [theme, setTheme] = (0, react_1.useState)(() => {
250
+ try {
251
+ const raw = window.localStorage.getItem('simulator:theme');
252
+ if (raw)
253
+ return { ...DEFAULT_THEME, ...JSON.parse(raw) };
254
+ }
255
+ catch (e) {
256
+ void e;
257
+ }
258
+ return DEFAULT_THEME;
259
+ });
260
+ // IframeResizer's React wrapper forwards a proxy object, not the raw iframe
261
+ // Store a generic ref and resolve the actual iframe window via a helper
262
+ const iframeRef = (0, react_1.useRef)(null);
263
+ const getIframeWindow = (0, react_1.useCallback)(() => {
264
+ const node = iframeRef.current;
265
+ if (!node)
266
+ return null;
267
+ if (node.contentWindow)
268
+ return node.contentWindow;
269
+ const proxy = node;
270
+ if (typeof proxy.getIframeElement === 'function') {
271
+ try {
272
+ const el = proxy.getIframeElement();
273
+ return el?.contentWindow ?? null;
274
+ }
275
+ catch (e) {
276
+ void e;
277
+ }
278
+ }
279
+ const maybeIframe = proxy.iframeResizer?.iframe;
280
+ if (maybeIframe?.contentWindow)
281
+ return maybeIframe.contentWindow;
282
+ return null;
283
+ }, []);
284
+ const loadTimeoutRef = (0, react_1.useRef)(null);
285
+ const APP_LOAD_TIMEOUT_MILLISECONDS = 8000;
286
+ const MAX_IFRAME_HEIGHT = 900;
287
+ const targetOrigin = (0, react_1.useMemo)(() => {
288
+ try {
289
+ return __ALLOW_ANY_ORIGIN__ ? '*' : window.location.origin;
290
+ }
291
+ catch {
292
+ return '*';
293
+ }
294
+ }, []);
295
+ const initialiseData = (0, react_1.useCallback)(() => {
296
+ const cw = getIframeWindow();
297
+ if (!cw) {
298
+ return;
299
+ }
300
+ const payload = {
301
+ type: 'extension-data',
302
+ data: {
303
+ __linkUrl: extensionData?.url ?? '',
304
+ __thumbnail: extensionData?.thumbnail ?? '',
305
+ __linkParams: null,
306
+ displayType: 'accordion',
307
+ ...extensionData,
308
+ __theme: { ...theme, backgroundColor: theme.buttonColor }, // button color of the host is the background color of the link app
309
+ },
310
+ };
311
+ cw.postMessage(payload, targetOrigin);
312
+ // cw.postMessage(payload, '*')
313
+ }, [targetOrigin, theme, extensionData, getIframeWindow]);
314
+ (0, react_1.useEffect)(() => {
315
+ const handler = (event) => {
316
+ const msg = (event?.data ?? {});
317
+ const type = msg.type || '';
318
+ if (!type)
319
+ return;
320
+ const cw = getIframeWindow();
321
+ if (cw && event.source !== cw)
322
+ return;
323
+ switch (type) {
324
+ case 'extension-loaded': {
325
+ initialiseData();
326
+ break;
327
+ }
328
+ case 'extension-ready': {
329
+ setLoading(false);
330
+ // Ensure data is sent if not already
331
+ try {
332
+ initialiseData();
333
+ }
334
+ catch (e) {
335
+ void e;
336
+ }
337
+ try {
338
+ const el = document.getElementById('extension-iframe-sim');
339
+ const cw = el?.contentWindow || getIframeWindow();
340
+ cw?.focus();
341
+ }
342
+ catch (e) {
343
+ void e;
344
+ }
345
+ break;
346
+ }
347
+ case 'extension-error': {
348
+ setLoading(false);
349
+ setLoadFailed(true);
350
+ break;
351
+ }
352
+ default:
353
+ break;
354
+ }
355
+ };
356
+ window.addEventListener('message', handler);
357
+ return () => window.removeEventListener('message', handler);
358
+ }, [initialiseData]);
359
+ (0, react_1.useEffect)(() => {
360
+ if (loading) {
361
+ if (loadTimeoutRef.current)
362
+ window.clearTimeout(loadTimeoutRef.current);
363
+ loadTimeoutRef.current = window.setTimeout(() => {
364
+ setLoadFailed(true);
365
+ }, APP_LOAD_TIMEOUT_MILLISECONDS);
366
+ }
367
+ return () => {
368
+ if (loadTimeoutRef.current)
369
+ window.clearTimeout(loadTimeoutRef.current);
370
+ };
371
+ }, [loading]);
372
+ // persist theme and re-post data when changed
373
+ (0, react_1.useEffect)(() => {
374
+ try {
375
+ window.localStorage.setItem('simulator:theme', JSON.stringify(theme));
376
+ }
377
+ catch (e) {
378
+ void e;
379
+ }
380
+ if (getIframeWindow())
381
+ initialiseData();
382
+ }, [theme, initialiseData, getIframeWindow]);
383
+ // Re-post data when external extension-data overrides change
384
+ (0, react_1.useEffect)(() => {
385
+ if (getIframeWindow())
386
+ initialiseData();
387
+ }, [extensionData, initialiseData, getIframeWindow]);
388
+ const iframeSrc = (0, react_1.useMemo)(() => `${window.location}?embed`, []);
389
+ const cardStyle = (0, react_1.useMemo)(() => {
390
+ const bg = theme.isOutlineStyle ? 'transparent' : theme.backgroundColor;
391
+ const border = `1px solid ${theme.borderColor}`;
392
+ const color = theme.textColor;
393
+ const radius = theme.borderRadius;
394
+ const style = {
395
+ background: bg,
396
+ color,
397
+ border,
398
+ borderRadius: radius,
399
+ };
400
+ return style;
401
+ }, [theme]);
402
+ return (react_1.default.createElement(react_1.default.Fragment, null,
403
+ react_1.default.createElement(Global, null),
404
+ react_1.default.createElement(Page, null,
405
+ react_1.default.createElement(Phone, { style: { background: theme.backgroundColor, color: theme.textColor } },
406
+ react_1.default.createElement(Notch, null),
407
+ react_1.default.createElement(HeaderChrome, null,
408
+ react_1.default.createElement(RoundIcon, { title: "Settings", onClick: () => setSettingsOpen((v) => !v), style: { cursor: 'pointer' } }, "\u2699\uFE0F"),
409
+ react_1.default.createElement(RoundIcon, { title: "Notifications" }, "\uD83D\uDD14")),
410
+ react_1.default.createElement(ScrollArea, null,
411
+ react_1.default.createElement(Profile, null,
412
+ react_1.default.createElement(Avatar, null, "SC"),
413
+ react_1.default.createElement(Name, null,
414
+ "Sample Creator ",
415
+ react_1.default.createElement(Badge, { title: "Verified" }, "\u2713")),
416
+ react_1.default.createElement(Bio, null, "Digital creator")),
417
+ react_1.default.createElement(ExtensionCard, { style: cardStyle },
418
+ loadFailed && (react_1.default.createElement("div", { style: {
419
+ position: 'absolute',
420
+ inset: 0,
421
+ display: 'grid',
422
+ placeItems: 'center',
423
+ background: 'rgba(31,17,71,0.06)',
424
+ color: '#92400e',
425
+ zIndex: 2,
426
+ } }, "Failed to load link app")),
427
+ loading && !loadFailed && (react_1.default.createElement("div", { style: {
428
+ position: 'absolute',
429
+ inset: 0,
430
+ display: 'grid',
431
+ placeItems: 'center',
432
+ background: 'rgba(31,17,71,0.03)',
433
+ color: '#4c1d95',
434
+ zIndex: 1,
435
+ } }, "Loading\u2026")),
436
+ react_1.default.createElement(iframe_resizer_react_1.default, { id: "extension-iframe-sim", style: { height: '0px', width: '1px', minWidth: '100%' }, maxHeight: MAX_IFRAME_HEIGHT, checkOrigin: false, src: iframeSrc, onResized: () => {
437
+ /* no-op: iFrameResizer manages height */
438
+ }, allow: ['clipboard-write'].join(' '), heightCalculationMethod: "max", sandbox: [
439
+ 'allow-popups',
440
+ 'allow-popups-to-escape-sandbox',
441
+ 'allow-top-navigation',
442
+ 'allow-same-origin',
443
+ 'allow-scripts',
444
+ 'allow-forms',
445
+ ].join(' '), forwardRef: (node) => {
446
+ iframeRef.current = node;
447
+ const cw = getIframeWindow();
448
+ if (cw) {
449
+ try {
450
+ setTimeout(() => initialiseData(), 0);
451
+ }
452
+ catch (e) {
453
+ void e;
454
+ }
455
+ }
456
+ } })),
457
+ react_1.default.createElement(Card, { style: { background: theme.buttonColor } },
458
+ react_1.default.createElement(CardIcon, null, "\uD83D\uDCBB"),
459
+ react_1.default.createElement("div", null,
460
+ react_1.default.createElement(CardTitle, null, "Featured link"),
461
+ react_1.default.createElement(CardSub, null, "example.com")),
462
+ react_1.default.createElement(Kebab, null, kebab())),
463
+ react_1.default.createElement(Card, { style: { background: theme.buttonColor } },
464
+ react_1.default.createElement(CardIcon, null, "\uD83D\uDCAC"),
465
+ react_1.default.createElement("div", null,
466
+ react_1.default.createElement(CardTitle, null, "Follow me")),
467
+ react_1.default.createElement(Kebab, null, kebab())),
468
+ react_1.default.createElement(Card, { style: { background: theme.buttonColor } },
469
+ react_1.default.createElement(CardIcon, null, "\u2709\uFE0F"),
470
+ react_1.default.createElement("div", null,
471
+ react_1.default.createElement(CardTitle, null, "Contact")),
472
+ react_1.default.createElement(Kebab, null, kebab())),
473
+ react_1.default.createElement(SocialRow, null,
474
+ react_1.default.createElement("span", { title: "Social A" }, "in"),
475
+ react_1.default.createElement("span", { title: "Social B" }, "\uD835\uDD4F"),
476
+ react_1.default.createElement("span", { title: "Social C" }, "\uD83D\uDC7B")),
477
+ react_1.default.createElement(Footer, null, "Report \u00B7 Privacy"))),
478
+ settingsOpen && (react_1.default.createElement(SettingsPanel, null,
479
+ react_1.default.createElement("div", { style: { fontWeight: 800, fontSize: 14, marginBottom: 8 } }, "Theme"),
480
+ [
481
+ ['Text color', 'textColor'],
482
+ ['Background', 'backgroundColor'],
483
+ ['Border color', 'borderColor'],
484
+ ['Hover text', 'textHoverColor'],
485
+ ['Contrast color', 'contrastColor'],
486
+ ['Button color', 'buttonColor'],
487
+ ].map(([label, key]) => (react_1.default.createElement("label", { key: key, style: {
488
+ display: 'grid',
489
+ gridTemplateColumns: '1fr 120px',
490
+ alignItems: 'center',
491
+ gap: 10,
492
+ fontSize: 12,
493
+ margin: '8px 0',
494
+ } },
495
+ react_1.default.createElement("span", null, label),
496
+ react_1.default.createElement("input", { type: "color", value: theme[key], onChange: (e) => setTheme({ ...theme, [key]: e.target.value }), style: { width: 120, height: 28, borderRadius: 8, border: '1px solid #d1d5db', background: '#fff' } })))),
497
+ react_1.default.createElement("label", { style: {
498
+ display: 'grid',
499
+ gridTemplateColumns: '1fr 120px',
500
+ alignItems: 'center',
501
+ gap: 10,
502
+ fontSize: 12,
503
+ margin: '8px 0',
504
+ } },
505
+ react_1.default.createElement("span", null, "Outline style"),
506
+ react_1.default.createElement("div", { style: { display: 'flex', justifyContent: 'flex-end' } },
507
+ react_1.default.createElement("input", { type: "checkbox", checked: theme.isOutlineStyle, onChange: (e) => setTheme({ ...theme, isOutlineStyle: e.target.checked }) }))))))));
508
+ }
509
+ exports.default = Simulator;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.9.0",
2
+ "version": "2.0.0-rc.1",
3
3
  "commands": {
4
4
  "build": {
5
5
  "id": "build",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linktr.ee/create-link-app",
3
- "version": "1.9.0",
3
+ "version": "2.0.0-rc.1",
4
4
  "description": "Create a Link App on Linktr.ee.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Linktree",
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "title": "Link App",
3
3
  "has_url": false,
4
+ "supports_featured_layout": true,
4
5
  "elements": [
5
6
  {
6
7
  "id": "url",
@@ -13,7 +13,8 @@
13
13
  "@linktr.ee/create-link-app": "latest",
14
14
  "@linktr.ee/ui-link-kit": "latest",
15
15
  "@types/react": "^18.2.8",
16
- "@types/react-dom": "^18.2.4"
16
+ "@types/react-dom": "^18.2.4",
17
+ "iframe-resizer-react": "^1.1.1"
17
18
  },
18
19
  "main": "lib/commonjs/index.js",
19
20
  "react-native": "src/index.ts",