@await-widget/runtime 0.0.48 → 0.0.50

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.
@@ -25,7 +25,6 @@ const crcTable = makeCrcTable();
25
25
  const bridgeInfoDirectory = '.await';
26
26
  const bridgeInfoFileName = 'bridge.json';
27
27
  const singleWidgetPath = '.';
28
- const widgetDirectoryError = 'Run await-widget from a package root or first-level widget folder whose package.json includes @await-widget/runtime.';
29
28
 
30
29
  function main() {
31
30
  const options = parseArgs(process.argv.slice(2));
@@ -139,16 +138,15 @@ function parseAppArgs(args) {
139
138
  }
140
139
 
141
140
  async function startWorkspaceServer(options) {
142
- let roots;
141
+ const workspaceRoot = process.cwd();
142
+ let widgetRoot;
143
143
  try {
144
- roots = await resolveWidgetServerRoots(process.cwd());
144
+ widgetRoot = await selectWidgetRoot(workspaceRoot);
145
145
  } catch (error) {
146
146
  console.error(error.message);
147
147
  process.exit(1);
148
148
  }
149
149
 
150
- const {packageRoot, widgetRoot} = roots;
151
-
152
150
  const token = crypto.randomBytes(6).toString('hex');
153
151
  const bridge = createBridgeState(widgetRoot);
154
152
  const server = http.createServer((request, response) => {
@@ -162,7 +160,7 @@ async function startWorkspaceServer(options) {
162
160
  server.listen(options.port, '0.0.0.0', () => {
163
161
  const address = server.address();
164
162
  const port = typeof address === 'object' && address ? address.port : options.port;
165
- writeBridgeInfo(packageRoot, port, token);
163
+ writeBridgeInfo(workspaceRoot, port, token);
166
164
  console.log(`Await computer connection serving ${widgetRoot}`);
167
165
  printServerUrls(port, token);
168
166
  console.log('Press Ctrl+C to stop.');
@@ -387,52 +385,34 @@ function widgetPathKey(widgets) {
387
385
  return widgets.map(widget => widget.path).sort((left, right) => left.localeCompare(right)).join('\n');
388
386
  }
389
387
 
390
- function findPackageRoot(start) {
388
+ function findBridgeRoot(start) {
391
389
  let current = path.resolve(start);
392
390
  while (true) {
393
- const candidate = path.join(current, 'package.json');
394
- if (fs.existsSync(candidate)) {
391
+ if (fs.existsSync(bridgeInfoPath(current))) {
395
392
  return current;
396
393
  }
397
394
 
398
395
  const parent = path.dirname(current);
399
396
  if (parent === current) {
400
- return null;
397
+ throw new Error('Cannot find Await bridge info. Run npx await-widget first.');
401
398
  }
402
399
 
403
400
  current = parent;
404
401
  }
405
402
  }
406
403
 
407
- async function resolveWidgetServerRoots(start) {
408
- const widgetRoot = path.resolve(start);
409
- const packageRoot = findPackageRoot(widgetRoot);
410
- if (
411
- !packageRoot
412
- || !hasRuntimeDependency(packageRoot)
413
- ) {
414
- throw new Error(widgetDirectoryError);
415
- }
416
-
417
- if (packageRoot === widgetRoot) {
418
- return {packageRoot, widgetRoot: await selectWidgetRoot(packageRoot)};
419
- }
420
-
421
- if (path.dirname(widgetRoot) !== packageRoot || !isValidTopLevelName(path.basename(widgetRoot))) {
422
- throw new Error(widgetDirectoryError);
423
- }
424
-
425
- return {packageRoot, widgetRoot};
426
- }
427
-
428
- function selectWidgetRoot(packageRoot) {
404
+ function selectWidgetRoot(directory) {
429
405
  const options = [
430
- {name: `${path.basename(packageRoot)} (root)`, path: packageRoot},
431
- ...fs.readdirSync(packageRoot, {withFileTypes: true})
432
- .filter(entry => entry.isDirectory() && isValidTopLevelName(entry.name))
406
+ {name: `${path.basename(directory)} (current directory)`, path: directory},
407
+ ...fs.readdirSync(directory, {withFileTypes: true})
408
+ .filter(entry => entry.isDirectory() && isSelectableDirectoryName(entry.name))
433
409
  .sort((left, right) => left.name.localeCompare(right.name))
434
- .map(entry => ({name: entry.name, path: path.join(packageRoot, entry.name)})),
410
+ .map(entry => ({name: entry.name, path: path.join(directory, entry.name)})),
435
411
  ];
412
+ if (options.length === 1) {
413
+ return directory;
414
+ }
415
+
436
416
  let selected = 0;
437
417
  let rendered = false;
438
418
  readline.emitKeypressEvents(process.stdin);
@@ -479,11 +459,6 @@ function selectWidgetRoot(packageRoot) {
479
459
  });
480
460
  }
481
461
 
482
- function hasRuntimeDependency(packageRoot) {
483
- const manifest = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
484
- return Boolean(manifest.dependencies?.['@await-widget/runtime'] ?? manifest.devDependencies?.['@await-widget/runtime']);
485
- }
486
-
487
462
  function listDesktopWidgets(root) {
488
463
  return [{name: path.basename(root), path: singleWidgetPath}];
489
464
  }
@@ -518,7 +493,7 @@ function safeWidgetPath(root, widgetPath, options = {}) {
518
493
  return root;
519
494
  }
520
495
 
521
- function isValidTopLevelName(name) {
496
+ function isSelectableDirectoryName(name) {
522
497
  return Boolean(name) && !name.includes('/') && !name.includes('\\') && !isExcludedName(name);
523
498
  }
524
499
 
@@ -766,19 +741,11 @@ function resolveBridgeUrl(options) {
766
741
  return options.bridgeUrl;
767
742
  }
768
743
 
769
- const root = findPackageRoot(path.resolve(options.workspace ?? process.cwd()));
770
- if (!root) {
771
- throw new Error('Cannot find package.json for Await bridge lookup.');
772
- }
773
-
744
+ const root = findBridgeRoot(options.workspace ?? process.cwd());
774
745
  return readBridgeUrl(bridgeInfoPath(root));
775
746
  }
776
747
 
777
748
  function readBridgeUrl(infoPath) {
778
- if (!fs.existsSync(infoPath)) {
779
- throw new Error('Cannot find Await bridge info. Run npx await-widget first.');
780
- }
781
-
782
749
  const info = JSON.parse(fs.readFileSync(infoPath, 'utf8'));
783
750
  return info.bridgeUrl;
784
751
  }
@@ -812,7 +779,8 @@ function appCommandResult(options, tool, result) {
812
779
  }
813
780
 
814
781
  function saveCapture(options, result) {
815
- const root = findPackageRoot(options.workspace ? path.resolve(options.workspace) : process.cwd()) ?? process.cwd();
782
+ const workspace = path.resolve(options.workspace ?? process.cwd());
783
+ const root = options.bridgeUrl ? workspace : findBridgeRoot(workspace);
816
784
  const directory = path.join(root, bridgeInfoDirectory, 'captures');
817
785
  const name = safeCaptureFileName(String(result.widgetId ?? 'widget'));
818
786
  const filePath = path.join(directory, `${name}-${Date.now()}.png`);
@@ -1021,14 +989,14 @@ function printHelp() {
1021
989
  await-widget [--port ${defaultPort}]
1022
990
  await-widget app <command> [options]
1023
991
 
1024
- Start the Await computer connection from a package root or first-level widget
1025
- folder whose package.json includes @await-widget/runtime. Starting from the
1026
- package root opens a directory selector.
992
+ Start the Await computer connection from any directory. If it has selectable
993
+ subdirectories, choose the current directory or one of them.
1027
994
 
1028
995
  What it does:
1029
996
  - Serves the selected widget folder to Await for live sync.
1030
997
  - Prints a URL to paste into Await's Connect Computer sheet.
1031
- - Writes .await/bridge.json in the package root for app commands.
998
+ - Writes .await/bridge.json in the directory where the connection was started.
999
+ - App commands search the current directory and its parents for bridge info.
1032
1000
  - Sends one-shot JSON app commands with await-widget app <command>.
1033
1001
 
1034
1002
  Examples:
@@ -1040,7 +1008,7 @@ Examples:
1040
1008
  npx await-widget app get-recent-widget-logs --widget-id YourWidget
1041
1009
 
1042
1010
  App command options:
1043
- --workspace <path> Read .await/bridge.json from another package/widget path.
1011
+ --workspace <path> Search this directory and its parents for .await/bridge.json.
1044
1012
  --bridge-url <url> Send directly to a bridge URL instead of reading bridge.json.
1045
1013
  --json <json> Merge raw JSON arguments into the app command.
1046
1014
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@await-widget/runtime",
3
- "version": "0.0.48",
3
+ "version": "0.0.50",
4
4
  "description": "TypeScript declarations for Await widgets.",
5
5
  "license": "MIT",
6
6
  "author": "Maundy",
package/types/await.d.ts CHANGED
@@ -10,293 +10,305 @@ declare module 'await' {
10
10
  props: Props,
11
11
  ) => NativeView;
12
12
  /** This is an experimental component and may change in the future. */
13
- export function Scroll(
13
+ export const Scroll: (
14
14
  props: ID &
15
15
  Mods & {
16
16
  children?: NativeView;
17
17
  },
18
- ): NativeView;
19
- export function VStack(
18
+ ) => NativeView;
19
+ export const VStack: (
20
20
  props: VStackValue &
21
21
  ID &
22
22
  Mods & {
23
23
  children?: NativeView;
24
24
  },
25
- ): NativeView;
26
- export function HStack(
25
+ ) => NativeView;
26
+ export const HStack: (
27
27
  props: HStackValue &
28
28
  ID &
29
29
  Mods & {
30
30
  children?: NativeView;
31
31
  },
32
- ): NativeView;
33
- export function Flow(
32
+ ) => NativeView;
33
+ export const Flow: (
34
34
  props: FlowValue &
35
35
  ID &
36
36
  Mods & {
37
37
  children?: NativeView;
38
38
  },
39
- ): NativeView;
40
- export function ZStack(
39
+ ) => NativeView;
40
+ export const ZStack: (
41
41
  props: ZStackValue &
42
42
  ID &
43
43
  Mods & {
44
44
  children?: NativeView;
45
45
  },
46
- ): NativeView;
46
+ ) => NativeView;
47
47
  /** Opens a custom-scheme URL or universal link through the Await app when tapped. */
48
- export function Link(
48
+ export const Link: (
49
49
  props: LinkValue &
50
50
  ID &
51
51
  Mods & {
52
52
  children?: NativeView;
53
53
  },
54
- ): NativeView;
54
+ ) => NativeView;
55
55
  /** When tapped, runs an intent or opens a universal link directly without going through the Await app. */
56
- export function Button(
56
+ export const Button: (
57
57
  props: ButtonValue &
58
58
  ID &
59
59
  Mods & {
60
60
  children?: NativeView;
61
61
  },
62
- ): NativeView;
62
+ ) => NativeView;
63
63
  /** Displays a solid color and expands to fill the available space. */
64
- export function Color(
64
+ export const Color: (
65
65
  props: ColorValue &
66
66
  ID &
67
67
  Mods & {
68
68
  children?: never;
69
69
  },
70
- ): NativeView;
70
+ ) => NativeView;
71
71
  /** Displays text and can contain nested `Text` or `Image` views. */
72
- export function Text(
72
+ export const Text: (
73
73
  props: TextValue &
74
74
  ID &
75
75
  Mods & {
76
76
  children?: NativeView;
77
77
  },
78
- ): NativeView;
78
+ ) => NativeView;
79
+ /**
80
+ Displays text in a fixed area, or as a rendered image when fontURL is set.
81
+ Without lines, lays out as much text as fits in the fixed area.
82
+ Supplied lines are drawn directly, including an empty array that draws no lines.
83
+ */
84
+ export const TextRender: (
85
+ props: TextRenderValue &
86
+ ID &
87
+ Mods & {
88
+ children?: never;
89
+ },
90
+ ) => NativeView;
79
91
  /** Displays an automatically updating clock, countdown/count-up timer, or stopwatch. */
80
- export function Time(
92
+ export const Time: (
81
93
  props: TimeValue &
82
94
  ID &
83
95
  Mods & {
84
96
  children?: never;
85
97
  },
86
- ): NativeView;
87
- export function ProgressView(
98
+ ) => NativeView;
99
+ export const ProgressView: (
88
100
  props: ProgressViewValue &
89
101
  ID &
90
102
  Mods & {
91
103
  children?: never;
92
104
  },
93
- ): NativeView;
94
- export function Gauge(
105
+ ) => NativeView;
106
+ export const Gauge: (
95
107
  props: GaugeValue &
96
108
  ID &
97
109
  Mods & {
98
110
  children?: never;
99
111
  },
100
- ): NativeView;
112
+ ) => NativeView;
101
113
  /** Displays an image from a local or remote path, or renders child views as an image. Avoid large or complex child content due to system limits. */
102
- export function Image(
114
+ export const Image: (
103
115
  props: ImageValue &
104
116
  ID &
105
117
  Mods & {
106
118
  children?: NativeView;
107
119
  },
108
- ): NativeView;
120
+ ) => NativeView;
109
121
  /** Displays a system SF Symbol. */
110
- export function Icon(
122
+ export const Icon: (
111
123
  props: IconValue &
112
124
  ID &
113
125
  Mods & {
114
126
  children?: never;
115
127
  },
116
- ): NativeView;
128
+ ) => NativeView;
117
129
  /** Displays an SVG from a local or remote path, or from raw SVG markup. */
118
- export function Svg(
130
+ export const Svg: (
119
131
  props: SvgValue &
120
132
  ID &
121
133
  Mods & {
122
134
  children?: never;
123
135
  },
124
- ): NativeView;
136
+ ) => NativeView;
125
137
  /** Draws a rounded rectangle that expands to fill the available space. */
126
- export function RoundedRectangle(
138
+ export const RoundedRectangle: (
127
139
  props: RoundedRectangleValue &
128
140
  ShapeValue &
129
141
  ID &
130
142
  Mods & {
131
143
  children?: never;
132
144
  },
133
- ): NativeView;
145
+ ) => NativeView;
134
146
  /** Draws a rounded rectangle with independently configurable corners that expands to fill the available space. */
135
- export function UnevenRoundedRectangle(
147
+ export const UnevenRoundedRectangle: (
136
148
  props: UnevenRoundedRectangleValue &
137
149
  ShapeValue &
138
150
  ID &
139
151
  Mods & {
140
152
  children?: never;
141
153
  },
142
- ): NativeView;
154
+ ) => NativeView;
143
155
  /** Draws a rectangle that expands to fill the available space. */
144
- export function Rectangle(
156
+ export const Rectangle: (
145
157
  props: ShapeValue &
146
158
  ID &
147
159
  Mods & {
148
160
  children?: never;
149
161
  },
150
- ): NativeView;
162
+ ) => NativeView;
151
163
  /** Draws a circular sector that expands to fill the available space. */
152
- export function Sector(
164
+ export const Sector: (
153
165
  props: SectorValue &
154
166
  ShapeValue &
155
167
  ID &
156
168
  Mods & {
157
169
  children?: never;
158
170
  },
159
- ): NativeView;
171
+ ) => NativeView;
160
172
  /** Draws an ellipse that expands to fill the available space. */
161
- export function Ellipse(
173
+ export const Ellipse: (
162
174
  props: ShapeValue &
163
175
  ID &
164
176
  Mods & {
165
177
  children?: never;
166
178
  },
167
- ): NativeView;
179
+ ) => NativeView;
168
180
  /** Draws a circle that expands to fill the available space. */
169
- export function Circle(
181
+ export const Circle: (
170
182
  props: ShapeValue &
171
183
  ID &
172
184
  Mods & {
173
185
  children?: never;
174
186
  },
175
- ): NativeView;
187
+ ) => NativeView;
176
188
  /** Draws a polygon that expands to fill the available space. */
177
- export function Polygon(
189
+ export const Polygon: (
178
190
  props: PolygonValue &
179
191
  ShapeValue &
180
192
  ID &
181
193
  Mods & {
182
194
  children?: never;
183
195
  },
184
- ): NativeView;
196
+ ) => NativeView;
185
197
  /** Draws a diamond that expands to fill the available space. */
186
- export function Diamond(
198
+ export const Diamond: (
187
199
  props: ShapeValue &
188
200
  ID &
189
201
  Mods & {
190
202
  children?: never;
191
203
  },
192
- ): NativeView;
204
+ ) => NativeView;
193
205
  /** Draws a capsule that expands to fill the available space. */
194
- export function Capsule(
206
+ export const Capsule: (
195
207
  props: CapsuleValue &
196
208
  ShapeValue &
197
209
  ID &
198
210
  Mods & {
199
211
  children?: never;
200
212
  },
201
- ): NativeView;
213
+ ) => NativeView;
202
214
  /** Adds flexible space within an `HStack` or `VStack`, expanding to fill the available space. */
203
- export function Spacer(
215
+ export const Spacer: (
204
216
  props: SpacerValue &
205
217
  ID &
206
218
  Mods & {
207
219
  children?: never;
208
220
  },
209
- ): NativeView;
210
- export function Group(
221
+ ) => NativeView;
222
+ export const Group: (
211
223
  props: ID &
212
224
  Mods & {
213
225
  children?: NativeView;
214
226
  },
215
- ): NativeView;
227
+ ) => NativeView;
216
228
  /** Creates a modifier for use in a `CustomButtonStyle`. */
217
- export function Modifier(
229
+ export const Modifier: (
218
230
  props: ID &
219
231
  Mods & {
220
232
  children?: never;
221
233
  },
222
- ): NativeView;
223
- export function EmptyView(
234
+ ) => NativeView;
235
+ export const EmptyView: (
224
236
  props: ID &
225
237
  Mods & {
226
238
  children?: never;
227
239
  },
228
- ): NativeView;
240
+ ) => NativeView;
229
241
  /** Creates a transparent button that expands to fill the available space. */
230
- export function FullButton(
242
+ export const FullButton: (
231
243
  props: ButtonValue &
232
244
  ID &
233
245
  Mods & {
234
246
  children?: never;
235
247
  },
236
- ): NativeView;
248
+ ) => NativeView;
237
249
  /** Displays a random number for debugging. */
238
- export function Stamp(
250
+ export const Stamp: (
239
251
  props: ID &
240
252
  Mods & {
241
253
  children?: never;
242
254
  },
243
- ): NativeView;
255
+ ) => NativeView;
244
256
  /** Creates a vertical page-flip effect. */
245
- export function VFlip(
257
+ export const VFlip: (
246
258
  props: FlipValue &
247
259
  ID &
248
260
  Mods & {
249
261
  children?: never;
250
262
  },
251
- ): NativeView;
263
+ ) => NativeView;
252
264
  /** Creates a horizontal page-flip effect. */
253
- export function HFlip(
265
+ export const HFlip: (
254
266
  props: FlipValue &
255
267
  ID &
256
268
  Mods & {
257
269
  children?: never;
258
270
  },
259
- ): NativeView;
271
+ ) => NativeView;
260
272
  /** Creates a flip transition around the x-axis. */
261
- export function XFlip(
273
+ export const XFlip: (
262
274
  props: AxisFlipValue &
263
275
  ID &
264
276
  Mods & {
265
277
  children?: never;
266
278
  },
267
- ): NativeView;
279
+ ) => NativeView;
268
280
  /** Creates a flip transition around the y-axis. */
269
- export function YFlip(
281
+ export const YFlip: (
270
282
  props: AxisFlipValue &
271
283
  ID &
272
284
  Mods & {
273
285
  children?: never;
274
286
  },
275
- ): NativeView;
287
+ ) => NativeView;
276
288
  /** Creates a GIF-like animation with limited system support. Only specific durations are available, transparency is not supported directly, and frame dimensions and count should remain small. */
277
- export function Gif(
289
+ export const Gif: (
278
290
  props: GifValue &
279
291
  ID &
280
292
  Mods & {
281
293
  children: NativeView[];
282
294
  },
283
- ): NativeView;
295
+ ) => NativeView;
284
296
  /** Cycles through a sequence of views based on the selected time unit, enabling effects such as a ticking second hand. Available on iOS 26 and later. */
285
- export function Ticker(
297
+ export const Ticker: (
286
298
  props: TickerValue &
287
299
  ID &
288
300
  Mods & {
289
301
  children: NativeView[];
290
302
  },
291
- ): NativeView;
303
+ ) => NativeView;
292
304
  /** Flashes once per second in a 60-second cycle, achieving the effect of a ticking second hand. Available on iOS 18 and later. */
293
- export function FlashTicker(
305
+ export const FlashTicker: (
294
306
  props: FlashTickerValue &
295
307
  ID &
296
308
  Mods & {
297
309
  children: NativeView[];
298
310
  },
299
- ): NativeView;
311
+ ) => NativeView;
300
312
  }
301
313
 
302
314
  declare module 'await/jsx-runtime' {
package/types/bridge.d.ts CHANGED
@@ -14,6 +14,13 @@ export declare function setInterval(
14
14
  export declare function sleep(ms: number): Promise<void>;
15
15
  export declare function clearInterval(timerID: unknown): void;
16
16
  export declare function print(...args: unknown[]): void;
17
+ /**
18
+ Returns whether two acyclic Encodable values are equal.
19
+ Records compare their own enumerable string keys, ignoring key order and distinguishing missing keys from undefined values.
20
+ Arrays compare their length and elements in order; empty slots are treated as undefined.
21
+ Primitive values use strict equality, except that NaN equals NaN.
22
+ */
23
+ export declare function compare(lhs: Encodable, rhs: Encodable): boolean;
17
24
  export declare const console: {
18
25
  log(...args: unknown[]): void;
19
26
  };
@@ -104,6 +111,16 @@ export declare const AwaitFile: {
104
111
  pages: number[],
105
112
  pageSize: number,
106
113
  ): Array<string | undefined>;
114
+ renderText(
115
+ path: string,
116
+ request: TextRenderRequest,
117
+ layout: TextRenderLayout,
118
+ ): TextRenderResult | undefined;
119
+ renderTexts(
120
+ path: string,
121
+ requests: TextRenderRequest[],
122
+ layout: TextRenderLayout,
123
+ ): Array<TextRenderResult | undefined> | undefined;
107
124
  saveUIRenderImage(path: string, value: NativeView): void;
108
125
  };
109
126
  export declare const AwaitAudio: {
@@ -215,6 +232,7 @@ declare global {
215
232
  const AwaitFile: AwaitFileGlobal;
216
233
  const AwaitAudio: AwaitAudioGlobal;
217
234
  function print(...args: unknown[]): void;
235
+ function compare(lhs: Encodable, rhs: Encodable): boolean;
218
236
  const sleep: SleepGlobal;
219
237
  }
220
238
  export {};
package/types/meta.d.ts CHANGED
@@ -14,17 +14,31 @@ type ProgressViewStyle = 'automatic' | 'linear' | 'circular';
14
14
 
15
15
  type Material = 'regular' | 'thin' | 'thick' | 'ultraThin' | 'ultraThick';
16
16
 
17
+ type Hierarchical =
18
+ | 'primary'
19
+ | 'secondary'
20
+ | 'tertiary'
21
+ | 'quaternary'
22
+ | 'quinary';
23
+
24
+ type ShapeStyleEffects = {
25
+ /**
26
+ Applies Apple's native inner shadow to this style.
27
+ Uses the source alpha; fully transparent fills do not produce a visible inner shadow.
28
+ Shadow color defaults to black at 33% opacity; blur, x, and y default to 0.
29
+ */
30
+ innerShadow?: Shadow;
31
+ };
32
+
33
+ /** The strings primary and secondary resolve as hierarchical styles in ShapeStyle positions. */
17
34
  type ShapeStyle =
18
35
  | Color
19
36
  | Material
37
+ | Hierarchical
20
38
  | LinearGradient
21
39
  | RadialGradient
22
40
  | AngularGradient
23
- | 'primary'
24
- | 'secondary'
25
- | 'tertiary'
26
- | 'quaternary'
27
- | 'quinary';
41
+ | ({style: Color | Material | Hierarchical} & ShapeStyleEffects);
28
42
 
29
43
  type NativeAnimation = (
30
44
  | {
@@ -127,7 +141,7 @@ type RadialGradient = {
127
141
  stops?: Array<[Color, number]>;
128
142
  startRadius?: number;
129
143
  endRadius?: number;
130
- };
144
+ } & ShapeStyleEffects;
131
145
 
132
146
  type AngularGradient = {
133
147
  gradient: 'angular';
@@ -136,7 +150,7 @@ type AngularGradient = {
136
150
  stops?: Array<[Color, number]>;
137
151
  angle?: number;
138
152
  center?: UnitPoint;
139
- };
153
+ } & ShapeStyleEffects;
140
154
 
141
155
  type LinearGradient = {
142
156
  gradient: 'linear';
@@ -145,7 +159,7 @@ type LinearGradient = {
145
159
  stops?: Stop[];
146
160
  startPoint?: UnitPoint;
147
161
  endPoint?: UnitPoint;
148
- };
162
+ } & ShapeStyleEffects;
149
163
 
150
164
  type UnitPoint =
151
165
  | 'center'
@@ -506,3 +520,13 @@ type DateFormatToken =
506
520
  | DateFormatFractionalSecondToken
507
521
  | DateFormatAMPMToken
508
522
  | DateFormatTimeZoneToken;
523
+
524
+ type ImageStroke = {
525
+ color: Color;
526
+ /**
527
+ Outward width in points at the decoded image's natural size. Must be nonnegative.
528
+ Adds transparent space around the image; the image and stroke then resize together.
529
+ proposedSize includes the added space. Template rendering also recolors the stroke.
530
+ */
531
+ width: number;
532
+ };
package/types/model.d.ts CHANGED
@@ -429,3 +429,56 @@ type Timeline<T extends Record<string, unknown> = Record<string, unknown>> = {
429
429
  update?: Update;
430
430
  skipOnPlayingNote?: boolean;
431
431
  };
432
+
433
+ type TextRenderLayout = {
434
+ width: number;
435
+ height: number;
436
+ fontSize: number;
437
+ fontWeight?: number;
438
+ fontWidth?: number;
439
+ lineHeight?: number;
440
+ lineSpacing?: number;
441
+ tracking?: number;
442
+ kerning?: number;
443
+ horizontalAlignment?: TextAlignment;
444
+ verticalAlignment?: 'top' | 'center' | 'bottom';
445
+ } & (
446
+ | {
447
+ fontURL: string;
448
+ fontName?: never;
449
+ fontDesign?: FontDesign;
450
+ }
451
+ | {
452
+ fontName: string;
453
+ fontURL?: never;
454
+ fontDesign?: FontDesign;
455
+ }
456
+ | {
457
+ fontURL?: never;
458
+ fontName?: never;
459
+ fontDesign?: FontDesign;
460
+ }
461
+ );
462
+
463
+ type TextRenderRequest = {
464
+ offset: number;
465
+ forward: boolean;
466
+ };
467
+
468
+ type TextRenderLine = {
469
+ range: [start: number, end: number];
470
+ baseline: number;
471
+ };
472
+
473
+ type TextRenderInput = TextRenderLayout & {
474
+ text: string;
475
+ lines?: TextRenderLine[];
476
+ };
477
+
478
+ type TextRenderResult = TextRenderLayout & {
479
+ text: string;
480
+ lines: TextRenderLine[];
481
+ startOffset: number;
482
+ endOffset: number;
483
+ totalBytes: number;
484
+ };
package/types/prop.d.ts CHANGED
@@ -117,6 +117,11 @@ type TextValue = {
117
117
  value?: Encodable;
118
118
  };
119
119
 
120
+ type TextRenderValue = {
121
+ value: TextRenderInput;
122
+ fill?: ShapeStyle;
123
+ };
124
+
120
125
  type GaugeValue = {
121
126
  /** Number value in range 0~1. */
122
127
  value?: number;
@@ -125,6 +130,8 @@ type GaugeValue = {
125
130
  type ImageValue = {
126
131
  /** Local or remote path such as `img.png` `path/img.png` `https://example.com/img.png` */
127
132
  url?: string;
133
+ /** Adds a round outer stroke along the image's alpha contour, including transparent holes. */
134
+ stroke?: ImageStroke;
128
135
  /** The modes used to resize an image to fit within its containing view. */
129
136
  resizable?: Resizable;
130
137
  /** The level of quality for rendering an image that requires interpolation, such as a scaled image. */
@@ -133,7 +140,7 @@ type ImageValue = {
133
140
  style?: TemplateRenderingMode;
134
141
  /** The mode that indicates how images are rendered on widget accented mode */
135
142
  accented?: Accented;
136
- /** The maximum number of pixels along the image's longest edge. Defaults to 1000. */
143
+ /** Decimals are truncated toward zero; negative values become 0. Positive integers limit image area to approximately maxPixel squared pixels. Use 0 for no limit, preserving source pixel dimensions. Defaults to 1000. */
137
144
  maxPixel?: number;
138
145
  /** Maximum width and height for the image's natural layout size. Scales down proportionally. */
139
146
  proposedSize?: {width?: number; height?: number};
@@ -344,6 +351,7 @@ type BaseMods = {
344
351
  };
345
352
  saturation?: number;
346
353
  scaleEffect?: ScaleEffect;
354
+ /** Ordinary outer shadow. For an inner shadow, set innerShadow on a ShapeStyle. */
347
355
  shadow?: Shadow;
348
356
  sides?: number;
349
357
  strikethrough?: