@trypattern/sdk 0.1.32 → 0.1.33

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,10 @@
1
+ import type { PickedFile } from '../files.ts';
2
+ import type { Placement } from './Placement.ts';
3
+ type FilePreviewProps = {
4
+ file: PickedFile;
5
+ alt: string;
6
+ aspect?: number;
7
+ style?: Placement;
8
+ };
9
+ export declare function FilePreview({ file, alt, aspect, style }: FilePreviewProps): import("react").JSX.Element;
10
+ export {};
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as stylex from '@stylexjs/stylex';
3
+ import { useEffect, useState } from 'react';
4
+ import { pickedFileOf } from "../files.js";
5
+ import { colors } from "../theme/tokens.stylex.js";
6
+ export function FilePreview({ file, alt, aspect = 1, style }) {
7
+ const [shown, setShown] = useState(null);
8
+ useEffect(() => {
9
+ const picked = pickedFileOf(file.id);
10
+ if (picked === undefined || !picked.type.startsWith('image/'))
11
+ return;
12
+ const url = URL.createObjectURL(picked);
13
+ setShown({ id: file.id, url });
14
+ return () => URL.revokeObjectURL(url);
15
+ }, [file.id]);
16
+ const url = shown?.id === file.id ? shown.url : null;
17
+ return (_jsx("span", { ...stylex.props(styles.frame, styles.ratio(aspect), style), children: url === null ? null : (_jsx("img", { alt: alt, src: url, ...stylex.props(styles.image) })) }));
18
+ }
19
+ const styles = stylex.create({
20
+ frame: {
21
+ position: 'relative',
22
+ display: 'block',
23
+ width: '100%',
24
+ overflow: 'hidden',
25
+ backgroundColor: colors.component
26
+ },
27
+ ratio: (ratio) => ({
28
+ aspectRatio: String(ratio)
29
+ }),
30
+ image: {
31
+ display: 'block',
32
+ width: '100%',
33
+ height: '100%',
34
+ objectFit: 'cover'
35
+ }
36
+ });
@@ -5,7 +5,8 @@ type ImageProps = {
5
5
  sizes: string;
6
6
  priority?: boolean;
7
7
  aspect?: number;
8
+ origin?: string;
8
9
  style?: Placement;
9
10
  };
10
- export declare function Image({ image, sizes, priority, aspect, style }: ImageProps): import("react").JSX.Element;
11
+ export declare function Image({ image, sizes, priority, aspect, origin, style }: ImageProps): import("react").JSX.Element;
11
12
  export {};
@@ -3,7 +3,7 @@ import * as stylex from '@stylexjs/stylex';
3
3
  import { useState } from 'react';
4
4
  import { imageSources, transformsOn } from "../media.js";
5
5
  import { breakpoints, colors, motion } from "../theme/tokens.stylex.js";
6
- export function Image({ image, sizes, priority = false, aspect, style }) {
6
+ export function Image({ image, sizes, priority = false, aspect, origin, style }) {
7
7
  const key = image?.key ?? null;
8
8
  const [seen, setSeen] = useState({
9
9
  key,
@@ -19,7 +19,10 @@ export function Image({ image, sizes, priority = false, aspect, style }) {
19
19
  }
20
20
  const sources = imageSources(image, {
21
21
  aspect,
22
- transforms: servedWithTransforms()
22
+ transforms: origin === undefined
23
+ ? servedWithTransforms()
24
+ : transformsOn(new URL(origin).hostname),
25
+ ...(origin === undefined ? {} : { origin: new URL(origin).origin })
23
26
  });
24
27
  return (_jsxs("span", { ...stylex.props(styles.frame, styles.ratio(ratio), style), children: [image.placeholder === null ? null : (_jsx("span", { "aria-hidden": "true", ...stylex.props(styles.placeholder(image.placeholder)) })), _jsx("img", { ref: (element) => {
25
28
  if (status === 'loading' &&
@@ -2,11 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Menu as BaseMenu } from '@base-ui/react/menu';
3
3
  import * as stylex from '@stylexjs/stylex';
4
4
  import { colors, controls, layers, space } from "../theme/tokens.stylex.js";
5
- import { controlSizes, focusRing, transparentLook } from "./controlLook.js";
5
+ import { buttonCorners, controlSizes, focusRing, transparentLook } from "./controlLook.js";
6
6
  import { MenuItems } from "./MenuItems.js";
7
7
  import { Popup } from "./Popup.js";
8
8
  export function Menu({ trigger, items, size = 'md', style }) {
9
- return (_jsxs(BaseMenu.Root, { children: [_jsx(BaseMenu.Trigger, { className: (state) => stylex.props(styles.trigger, controlSizes[size], triggerPaddings[size], transparentLook.rest, transparentLook.hoverable, state.open && transparentLook.open, styles.triggerText, focusRing.visible, style).className, children: trigger }), _jsx(BaseMenu.Portal, { children: _jsx(BaseMenu.Positioner, { side: "bottom", align: "start", sideOffset: 4, className: stylex.props(styles.positioner).className, children: _jsx(BaseMenu.Popup, { render: (props, state) => (_jsx(Popup, { ...props, kind: "menu", isOpen: state.open, transitionStatus: state.transitionStatus })), children: _jsx(MenuItems, { items: items, primitive: "Menu" }) }) }) })] }));
9
+ return (_jsxs(BaseMenu.Root, { children: [_jsx(BaseMenu.Trigger, { className: (state) => stylex.props(styles.trigger, controlSizes[size], buttonCorners[size], triggerPaddings[size], transparentLook.rest, transparentLook.hoverable, state.open && transparentLook.open, styles.triggerText, focusRing.visible, style).className, children: trigger }), _jsx(BaseMenu.Portal, { children: _jsx(BaseMenu.Positioner, { side: "bottom", align: "start", sideOffset: 4, className: stylex.props(styles.positioner).className, children: _jsx(BaseMenu.Popup, { render: (props, state) => (_jsx(Popup, { ...props, kind: "menu", isOpen: state.open, transitionStatus: state.transitionStatus })), children: _jsx(MenuItems, { items: items, primitive: "Menu" }) }) }) })] }));
10
10
  }
11
11
  const styles = stylex.create({
12
12
  trigger: {
@@ -4,13 +4,13 @@ import { RadioGroup } from '@base-ui/react/radio-group';
4
4
  import * as stylex from '@stylexjs/stylex';
5
5
  import { borders, colors, fontWeights, motion, radii } from "../theme/tokens.stylex.js";
6
6
  import { interact } from "./interaction.js";
7
- import { controlSizes, focusRing } from "./controlLook.js";
7
+ import { buttonCorners, controlSizes, focusRing } from "./controlLook.js";
8
8
  export function SegmentedControl({ label, value, options, size = 'md', disabled = false, onChange, style }) {
9
9
  const index = Math.max(0, options.findIndex((option) => option.value === value));
10
10
  return (_jsxs(RadioGroup, { "aria-label": label, value: value, disabled: disabled, onValueChange: (next) => {
11
11
  interact({ primitive: 'SegmentedControl', event: 'change', label });
12
12
  onChange(next);
13
- }, className: stylex.props(styles.group, style).className, children: [_jsx("span", { ...stylex.props(styles.indicator(index, options.length)) }), options.map((option) => (_jsx(Radio.Root, { value: option.value, className: (state) => stylex.props(styles.option, controlSizes[size], !disabled && styles.hoverable, state.checked && styles.optionChecked, disabled && styles.disabled, focusRing.visible).className, children: option.label }, option.value)))] }));
13
+ }, className: stylex.props(styles.group, style).className, children: [_jsx("span", { ...stylex.props(styles.indicator(index, options.length)) }), options.map((option) => (_jsx(Radio.Root, { value: option.value, className: (state) => stylex.props(styles.option, controlSizes[size], buttonCorners[size], !disabled && styles.hoverable, state.checked && styles.optionChecked, disabled && styles.disabled, focusRing.visible).className, children: option.label }, option.value)))] }));
14
14
  }
15
15
  const styles = stylex.create({
16
16
  group: {
@@ -4,6 +4,7 @@ export const viewports = {
4
4
  compact: { width: 390, height: 844, deviceScaleFactor: 2 }
5
5
  };
6
6
  export const viewportNames = ['desktop', 'compact'];
7
+ const STAND_IN_PHOTO = '<svg xmlns="http://www.w3.org/2000/svg" width="4" height="3"><rect width="4" height="3" fill="#d9d4c8"/></svg>';
7
8
  const QUIET_NETWORK_MS = 250;
8
9
  const SETTLE_TIMEOUT_MS = 10_000;
9
10
  const HOOK_TIMEOUT_MS = 10_000;
@@ -122,6 +123,12 @@ export async function openApp(browser, baseUrl, path, viewport, signedInAs, memo
122
123
  });
123
124
  }, { ...memory, signedInAs });
124
125
  }
126
+ await context.route((url) => url.origin !== new URL(baseUrl).origin, (route) => route.request().resourceType() === 'image'
127
+ ? route.fulfill({
128
+ contentType: 'image/svg+xml',
129
+ body: STAND_IN_PHOTO
130
+ })
131
+ : route.continue());
125
132
  const page = await context.newPage();
126
133
  const problems = [];
127
134
  recordProblems(page, problems);
package/dist/media.d.ts CHANGED
@@ -40,7 +40,8 @@ export type ImageSources = {
40
40
  width: number;
41
41
  height: number;
42
42
  };
43
- export declare function imageSources(image: MediaImage, { aspect, transforms }: {
43
+ export declare function imageSources(image: MediaImage, { aspect, transforms, origin }: {
44
44
  aspect?: number | undefined;
45
45
  transforms: boolean;
46
+ origin?: string;
46
47
  }): ImageSources;
package/dist/media.js CHANGED
@@ -46,14 +46,14 @@ function transformed(key, width, aspect) {
46
46
  ];
47
47
  return `/cdn-cgi/image/${options.join(',')}${mediaPath(key)}`;
48
48
  }
49
- export function imageSources(image, { aspect, transforms }) {
49
+ export function imageSources(image, { aspect, transforms, origin = '' }) {
50
50
  const largest = largestWidth(image, aspect);
51
51
  const height = aspect === undefined
52
52
  ? image.height
53
53
  : Math.max(1, Math.round(largest / aspect));
54
54
  if (!transforms) {
55
55
  return {
56
- src: mediaPath(image.key),
56
+ src: `${origin}${mediaPath(image.key)}`,
57
57
  srcSet: undefined,
58
58
  width: largest,
59
59
  height
@@ -64,9 +64,9 @@ export function imageSources(image, { aspect, transforms }) {
64
64
  largest
65
65
  ].filter((width, index, all) => all.indexOf(width) === index);
66
66
  return {
67
- src: transformed(image.key, Math.min(FALLBACK_WIDTH, largest), aspect),
67
+ src: `${origin}${transformed(image.key, Math.min(FALLBACK_WIDTH, largest), aspect)}`,
68
68
  srcSet: widths
69
- .map((width) => `${transformed(image.key, width, aspect)} ${width}w`)
69
+ .map((width) => `${origin}${transformed(image.key, width, aspect)} ${width}w`)
70
70
  .join(', '),
71
71
  width: largest,
72
72
  height
package/dist/react.d.ts CHANGED
@@ -25,6 +25,7 @@ export { Form } from './components/Form.tsx';
25
25
  export { Icon } from './components/Icon.tsx';
26
26
  export { IconButton } from './components/IconButton.tsx';
27
27
  export { FilePicker } from './components/FilePicker.tsx';
28
+ export { FilePreview } from './components/FilePreview.tsx';
28
29
  export { Gallery } from './components/Gallery.tsx';
29
30
  export { Header } from './components/Header.tsx';
30
31
  export { Image } from './components/Image.tsx';
package/dist/react.js CHANGED
@@ -23,6 +23,7 @@ export { Form } from "./components/Form.js";
23
23
  export { Icon } from "./components/Icon.js";
24
24
  export { IconButton } from "./components/IconButton.js";
25
25
  export { FilePicker } from "./components/FilePicker.js";
26
+ export { FilePreview } from "./components/FilePreview.js";
26
27
  export { Gallery } from "./components/Gallery.js";
27
28
  export { Header } from "./components/Header.js";
28
29
  export { Image } from "./components/Image.js";
@@ -66,6 +66,7 @@ const PRIMITIVES = new Set([
66
66
  'CopyButton',
67
67
  'EmptyState',
68
68
  'FilePicker',
69
+ 'FilePreview',
69
70
  'Form',
70
71
  'Gallery',
71
72
  'Header',
@@ -100,6 +100,9 @@ export function seedSql(tables, seed) {
100
100
  });
101
101
  return [...users, ...rowsSql].join('\n');
102
102
  }
103
+ function startedAt(secondsAgo) {
104
+ return `strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-${secondsAgo} seconds')`;
105
+ }
103
106
  export function starterSql(tables, rows) {
104
107
  return Object.values(tables)
105
108
  .flatMap((table) => {
@@ -108,7 +111,11 @@ export function starterSql(tables, rows) {
108
111
  return [];
109
112
  const keys = ['id', 'createdAt', ...Object.keys(table.columns)];
110
113
  const values = listed
111
- .map((row) => `(${keys.map((key) => sqlLiteral((row[key] ?? null))).join(', ')})`)
114
+ .map((row, index) => `(${keys
115
+ .map((key) => key === 'createdAt'
116
+ ? startedAt(listed.length - 1 - index)
117
+ : sqlLiteral((row[key] ?? null)))
118
+ .join(', ')})`)
112
119
  .join(',\n ');
113
120
  return [
114
121
  `INSERT OR IGNORE INTO ${snakeCase(table.name)} (${keys.map(snakeCase).join(', ')}) VALUES\n ${values};`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trypattern/sdk",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",