@tin.so/core 0.1.0 → 0.2.0
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/LICENSE +21 -0
- package/README.md +96 -8
- package/THIRD_PARTY_NOTICES.md +61 -0
- package/dist/{index-CtUX1lFe.d.mts → index-blYEvwPp.d.mts} +65 -18
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/node.d.mts +2 -2
- package/dist/node.mjs +2 -2
- package/dist/{src-BdXa6abO.mjs → src-VhDXsdB0.mjs} +232 -116
- package/package.json +16 -15
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Chris Cook
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,33 +1,121 @@
|
|
|
1
1
|
# @tin.so/core
|
|
2
2
|
|
|
3
|
-
Renders code images from a JSON spec
|
|
3
|
+
Renders code images from a JSON spec, to SVG and PNG. One image can show several snippets: windows next to each other or below each other, panes in a window, and popovers that point to a word. It has all [ray.so](https://ray.so) themes, line highlights, text styles and colors, tabs with file icons, and fixed aspect ratios for social media.
|
|
4
|
+
|
|
5
|
+
The pipeline is `spec → Shiki tokens → layout → SVG → PNG` and needs no browser. The layout puts every character on a monospace grid with the metrics of the bundled fonts, and [resvg](https://github.com/linebender/resvg) (WebAssembly) rasterizes the SVG. The same code runs in Node and in the browser.
|
|
6
|
+
|
|
7
|
+
This package is the renderer of the [tin.so](https://tin.so) web editor and of the [`tin` CLI](https://www.npmjs.com/package/tin.so). To make images from the command line, use the CLI.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @tin.so/core
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Node 22.18 or later.
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
4
18
|
|
|
5
19
|
```ts
|
|
20
|
+
import { writeFile } from "node:fs/promises";
|
|
6
21
|
import { render } from "@tin.so/core/node";
|
|
7
22
|
|
|
8
23
|
const png = await render({
|
|
9
24
|
theme: "vercel",
|
|
10
25
|
windows: [
|
|
11
26
|
{
|
|
12
|
-
titleBar: false,
|
|
13
27
|
tabs: true,
|
|
14
|
-
|
|
28
|
+
titleBar: false,
|
|
15
29
|
panes: [
|
|
16
|
-
{
|
|
30
|
+
{
|
|
31
|
+
title: "main.ts",
|
|
32
|
+
code: 'import plans from "./plans.toml";\n\nplans.free.price;',
|
|
33
|
+
highlight: [3],
|
|
34
|
+
annotations: [
|
|
35
|
+
{ line: 1, match: "plans", code: "const plans: {\n free: { price: 0 };\n}" },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
17
38
|
{ title: "plans.toml", code: "[free]\nprice = 0" },
|
|
18
39
|
],
|
|
19
40
|
},
|
|
20
41
|
],
|
|
21
42
|
});
|
|
43
|
+
|
|
44
|
+
await writeFile("plans.png", png);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`render(spec, { format: "svg" })` gives an SVG with the fonts inside it. `scale` sets the pixel ratio of a PNG (default 2). By default, the image contains the spec and a link that opens it in the web editor. `metadata: false` turns this off.
|
|
48
|
+
|
|
49
|
+
`createLink(spec)` gives that link without an image:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { createLink } from "@tin.so/core";
|
|
53
|
+
|
|
54
|
+
const link = await createLink(spec); // https://tin.so/#spec=…
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## The spec
|
|
58
|
+
|
|
59
|
+
A spec is JSON with the image settings, its windows, the panes of each window, and the code of each pane. A spec needs only `windows`, `panes` and `code`.
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"$schema": "https://tin.so/schema/v1.json",
|
|
64
|
+
"theme": "candy",
|
|
65
|
+
"windows": [{ "panes": [{ "title": "main.ts", "code": "const a = 1;" }] }]
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| Level | Fields |
|
|
70
|
+
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
71
|
+
| Image | `theme`, `mode`, `background`, `padding`, `font`, `lineNumbers`, `direction`, `gap`, `aspectRatio`, `width`, `height`, `windows` |
|
|
72
|
+
| Window | `panes`, `split`, `titleBar`, `tabs`, `frame`, `controls`, `title`, `width`, `height`, `x`, `y` |
|
|
73
|
+
| Pane | `code`, `title`, `language`, `highlight`, `marks` (bold, italic, colors, highlighter fills), `annotations` (popovers), `icon`, `lineNumbers` |
|
|
74
|
+
|
|
75
|
+
The full spec, with a description, a default and the limits of each field, is the JSON Schema at [`https://tin.so/schema/v1.json`](https://tin.so/schema/v1.json). `specJsonSchema()` gives the same schema.
|
|
76
|
+
|
|
77
|
+
The `$schema` URL (`/schema/v1.json`) or the field `v` gives the version of the spec. A spec without both is version 1. A new version comes only with a change that can break a spec. New optional fields and new themes, fonts or languages come without one.
|
|
78
|
+
|
|
79
|
+
The renderer ignores keys that start with `x-` in every object, so a spec can carry data of other tools.
|
|
80
|
+
|
|
81
|
+
An invalid spec throws a `SpecError` that names each problem with its path, for example `windows.0.panes.0.annotations.0.match: "plnas" (occurrence 1) is not in line 1`.
|
|
82
|
+
|
|
83
|
+
## API
|
|
84
|
+
|
|
85
|
+
| Function | What it does |
|
|
86
|
+
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
87
|
+
| `render(spec, options?)` | Renders a spec to PNG or SVG bytes, in Node (`@tin.so/core/node`) |
|
|
88
|
+
| `parseSpec(input)` | Checks a spec and fills in the defaults. Throws a `SpecError` |
|
|
89
|
+
| `layout(spec, options?)` | Highlights the code and places the windows, panes and popovers. Gives a `Scene` |
|
|
90
|
+
| `toSvg(scene, options?)` | Writes the SVG of a scene, with or without embedded fonts and metadata |
|
|
91
|
+
| `initPng(load)`, `svgToPng(svg, fonts, options?)` | Loads the resvg WebAssembly once, then rasterizes an SVG |
|
|
92
|
+
| `loadSceneFonts(scene, loadFont)` | Loads the font files that a scene uses |
|
|
93
|
+
| `createLink(spec, appUrl?)`, `readLink(link)` | Writes a spec into an editor link, and reads it back |
|
|
94
|
+
| `encodeSpec(spec)`, `decodeSpec(encoded)` | The compressed spec of a link, without the URL |
|
|
95
|
+
| `readImageSpec(image)` | Reads the spec from a PNG or SVG that has the metadata. Gives `undefined` for other images |
|
|
96
|
+
| `specJsonSchema()`, `SPEC_SCHEMA_URL` | The JSON Schema of the spec, and its URL |
|
|
97
|
+
| `THEME_IDS`, `CODE_FONT_IDS`, `LANGUAGE_IDS`, `ICON_IDS` | The ids that the spec accepts |
|
|
98
|
+
|
|
99
|
+
### In the browser
|
|
100
|
+
|
|
101
|
+
Import from `@tin.so/core`, and give the steps that read files a loader, because the browser has no file system:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { initPng, layout, loadSceneFonts, svgToPng, toSvg } from "@tin.so/core";
|
|
105
|
+
|
|
106
|
+
const scene = await layout(spec);
|
|
107
|
+
// loadFont gets a path in the fonts/ directory of this package, for example "fira-code/fira-code-400.ttf"
|
|
108
|
+
const fonts = await loadSceneFonts(scene, loadFont);
|
|
109
|
+
await initPng(() => fetch(wasmUrl)); // the index_bg.wasm file of @resvg/resvg-wasm
|
|
110
|
+
const png = await svgToPng(toSvg(scene), fonts.values());
|
|
22
111
|
```
|
|
23
112
|
|
|
24
113
|
## Scripts
|
|
25
114
|
|
|
26
115
|
- `pnpm port-themes` fetches the ray.so themes at a pinned commit and writes `src/themes/*.json`.
|
|
27
116
|
- `pnpm download-fonts` downloads the fonts to `fonts/` and writes their metrics to `src/fonts/fonts.generated.json`.
|
|
117
|
+
- `pnpm generate-icons` writes the language icons from Simple Icons.
|
|
28
118
|
|
|
29
|
-
##
|
|
119
|
+
## License
|
|
30
120
|
|
|
31
|
-
|
|
32
|
-
- The fonts are under the SIL Open Font License, except Commit Mono (MIT). Each font directory contains its license.
|
|
33
|
-
- The file icon is from [Lucide](https://lucide.dev) (ISC), the language icons from [Simple Icons](https://simpleicons.org) (CC0).
|
|
121
|
+
MIT. The themes come from ray.so (MIT), the file icon from Lucide (ISC), the language icons from Simple Icons (CC0), and the fonts are under the SIL Open Font License, except Commit Mono (MIT). See [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
`@tin.so/core` contains work of other projects. Their licenses follow.
|
|
4
|
+
|
|
5
|
+
## ray.so
|
|
6
|
+
|
|
7
|
+
The themes, the token colors and the frame styles come from [ray.so](https://github.com/raycast/ray-so).
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
MIT License
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2024 Raycast Technologies Ltd
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Lucide
|
|
34
|
+
|
|
35
|
+
The file icon comes from [Lucide](https://lucide.dev).
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
ISC License
|
|
39
|
+
|
|
40
|
+
Copyright (c) 2026 Lucide Icons and Contributors
|
|
41
|
+
|
|
42
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
43
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
44
|
+
copyright notice and this permission notice appear in all copies.
|
|
45
|
+
|
|
46
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
47
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
48
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
49
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
50
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
51
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
52
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Simple Icons
|
|
56
|
+
|
|
57
|
+
The language icons come from [Simple Icons](https://simpleicons.org), under [CC0 1.0](https://github.com/simple-icons/simple-icons/blob/develop/LICENSE.md).
|
|
58
|
+
|
|
59
|
+
## Fonts
|
|
60
|
+
|
|
61
|
+
The fonts in `fonts/` are under the SIL Open Font License 1.1, except Commit Mono, which is under the MIT License. Each font directory contains its license.
|
|
@@ -77,25 +77,38 @@ declare const SPEC_SCHEMA_URL: string;
|
|
|
77
77
|
* the spec, such as line ranges, so their rules are only in the descriptions.
|
|
78
78
|
*/
|
|
79
79
|
declare function specJsonSchema(): Record<string, unknown>;
|
|
80
|
+
/**
|
|
81
|
+
* True when the value is the URL of a tin.so spec schema: a path that ends with
|
|
82
|
+
* /schema/v<number>.json on the host of the hosted app, on an earlier host of the app, or on the
|
|
83
|
+
* host of the given app URL (for example a local editor).
|
|
84
|
+
*/
|
|
85
|
+
declare function isSpecSchemaUrl(value: unknown, appUrl?: string): boolean;
|
|
80
86
|
/**
|
|
81
87
|
* The hosted web app. The CLI takes another app from its TIN_APP_URL environment variable,
|
|
82
88
|
* for example the local editor during development.
|
|
83
89
|
*/
|
|
84
|
-
declare const DEFAULT_APP_URL = "https://
|
|
90
|
+
declare const DEFAULT_APP_URL = "https://tin.so/";
|
|
91
|
+
type DecodeOptions = {
|
|
92
|
+
/** The largest size of the inflated spec in bytes, so that a hostile input cannot use much memory */
|
|
93
|
+
maxBytes?: number;
|
|
94
|
+
};
|
|
85
95
|
/**
|
|
86
96
|
* Compresses a spec into a URL-safe string. A spec without a version gets version 1, so that the
|
|
87
97
|
* link keeps its meaning when a later version changes the format.
|
|
88
98
|
*/
|
|
89
99
|
declare function encodeSpec(spec: unknown): Promise<string>;
|
|
90
|
-
/**
|
|
91
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Reads a spec from a string of the encoder, with the fields of older links changed. Throws for
|
|
102
|
+
* bad data and for a spec larger than the limit.
|
|
103
|
+
*/
|
|
104
|
+
declare function decodeSpec(encoded: string, options?: DecodeOptions): Promise<unknown>;
|
|
92
105
|
/**
|
|
93
106
|
* A link that opens the spec in the web app. The spec is in the fragment, so it never goes to a
|
|
94
107
|
* server and has no length limit of a request line.
|
|
95
108
|
*/
|
|
96
109
|
declare function createLink(spec: unknown, appUrl?: string): Promise<string>;
|
|
97
110
|
/** Reads the spec from the fragment of a link, or returns undefined when the fragment has none */
|
|
98
|
-
declare function readLink(link: string): Promise<unknown>;
|
|
111
|
+
declare function readLink(link: string, options?: DecodeOptions): Promise<unknown>;
|
|
99
112
|
type Mode = "light" | "dark";
|
|
100
113
|
/** Token colors of a theme. Keys that are missing use the foreground color. */
|
|
101
114
|
type Syntax = {
|
|
@@ -332,14 +345,16 @@ declare const SOFTWARE = "tin.so";
|
|
|
332
345
|
declare const PNG_KEYWORDS: {
|
|
333
346
|
readonly software: "Software";
|
|
334
347
|
readonly source: "Source";
|
|
335
|
-
readonly link: "
|
|
336
|
-
readonly spec: "code-image:spec";
|
|
348
|
+
readonly link: "tin.so:link";
|
|
337
349
|
};
|
|
338
350
|
/** The namespace of the metadata element in an SVG */
|
|
339
|
-
declare const SVG_NAMESPACE = "urn:
|
|
340
|
-
/**
|
|
351
|
+
declare const SVG_NAMESPACE = "urn:tin.so:metadata:1";
|
|
352
|
+
/**
|
|
353
|
+
* The data that an exported image carries, so that the editor can open the image again. The image
|
|
354
|
+
* stores only the link, because the link holds the whole spec.
|
|
355
|
+
*/
|
|
341
356
|
type ImageMetadata = {
|
|
342
|
-
/** The spec input, as the user wrote it */
|
|
357
|
+
/** The spec input, as the user wrote it, with its version */
|
|
343
358
|
spec: unknown;
|
|
344
359
|
/** A link that opens the spec in the editor */
|
|
345
360
|
link: string;
|
|
@@ -350,8 +365,8 @@ type ImageMetadata = {
|
|
|
350
365
|
*/
|
|
351
366
|
declare function createImageMetadata(spec: unknown, appUrl?: string): Promise<ImageMetadata>;
|
|
352
367
|
/**
|
|
353
|
-
* Adds text chunks after the header of a PNG: the program, the app URL
|
|
354
|
-
*
|
|
368
|
+
* Adds text chunks after the header of a PNG: the program, the app URL and the editor link. A PNG
|
|
369
|
+
* without the expected header comes back unchanged.
|
|
355
370
|
*/
|
|
356
371
|
declare function addPngMetadata(png: Uint8Array, metadata: ImageMetadata): Promise<Uint8Array>;
|
|
357
372
|
/**
|
|
@@ -361,6 +376,16 @@ declare function addPngMetadata(png: Uint8Array, metadata: ImageMetadata): Promi
|
|
|
361
376
|
*/
|
|
362
377
|
declare function readImageSpec(input: Uint8Array | ArrayBuffer | Blob | string): Promise<unknown>;
|
|
363
378
|
declare const IMPLICIT_SPEC_VERSION = 1;
|
|
379
|
+
/**
|
|
380
|
+
* The version that the $schema URL of a spec names, or undefined when the spec has no such URL.
|
|
381
|
+
* Only the path counts, so that the URL of each host that serves the schema gives the version.
|
|
382
|
+
*/
|
|
383
|
+
declare function schemaVersion(spec: unknown): number | undefined;
|
|
384
|
+
/**
|
|
385
|
+
* Keys with this prefix hold data of other tools, such as an editor state. The renderer ignores
|
|
386
|
+
* them in each object of a spec, so that a spec with such keys stays valid in all versions.
|
|
387
|
+
*/
|
|
388
|
+
declare const EXTENSION_PREFIX = "x-";
|
|
364
389
|
/**
|
|
365
390
|
* Changes the fields of older specs from before the v field to the current fields, so that old
|
|
366
391
|
* links, files and images stay valid. Input that is not a spec stays as it is, so that the spec
|
|
@@ -369,7 +394,8 @@ declare const IMPLICIT_SPEC_VERSION = 1;
|
|
|
369
394
|
declare function migrateLegacySpec(spec: unknown): unknown;
|
|
370
395
|
/**
|
|
371
396
|
* Adds the version to a spec without one, so that a stored spec keeps its meaning when a later
|
|
372
|
-
* version changes the format.
|
|
397
|
+
* version changes the format. The version comes from the $schema URL, or is version 1 without it.
|
|
398
|
+
* A spec with a version stays as it is.
|
|
373
399
|
*/
|
|
374
400
|
declare function stampSpecVersion(spec: unknown): unknown;
|
|
375
401
|
type PngOptions = {
|
|
@@ -400,12 +426,19 @@ declare const SPEC_LIMITS: {
|
|
|
400
426
|
readonly size: 8192;
|
|
401
427
|
};
|
|
402
428
|
declare const MARK_STYLES: readonly ["strong", "emphasis", "underline", "strikethrough"];
|
|
429
|
+
declare const ANNOTATION_TYPES: readonly ["popover"];
|
|
403
430
|
/**
|
|
404
431
|
* Versions of the spec format.
|
|
405
432
|
*
|
|
406
433
|
* - Increase v only for a breaking change: a field that changes its meaning, gets a new name or
|
|
407
|
-
* goes away
|
|
408
|
-
*
|
|
434
|
+
* goes away, a changed default, a value that a list no longer has (a theme, font, icon or
|
|
435
|
+
* language id), or a smaller limit. Specs leave out the defaults, so a new default changes old
|
|
436
|
+
* images. A new optional field, a new value or a larger limit does not increase v, because older
|
|
437
|
+
* specs stay valid.
|
|
438
|
+
* - The version is v, or the version of the $schema URL (/schema/v<number>.json) without v. When
|
|
439
|
+
* both are there, they must be the same. A spec without both is version 1, now and in all later
|
|
440
|
+
* versions, because older specs have neither.
|
|
441
|
+
* - Keys that start with "x-" are for other tools. The renderer ignores them in every object.
|
|
409
442
|
* - For a new version, add its schema (with v as a required literal of the new number) to
|
|
410
443
|
* SPEC_SCHEMAS, a function that changes a parsed spec of the version before into the new
|
|
411
444
|
* version to MIGRATIONS, and increase LATEST_SPEC_VERSION. The schema of the latest version is
|
|
@@ -446,6 +479,7 @@ declare const SpecSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
|
446
479
|
readonly icon: v.SchemaWithPipe<readonly [v.OptionalSchema<v.PicklistSchema<["auto", "none", ...string[]], undefined>, "auto">, v.DescriptionAction<string, string>]>;
|
|
447
480
|
readonly lineNumbers: v.SchemaWithPipe<readonly [v.OptionalSchema<v.BooleanSchema<undefined>, undefined>, v.DescriptionAction<boolean | undefined, string>]>;
|
|
448
481
|
readonly annotations: v.SchemaWithPipe<readonly [v.OptionalSchema<v.SchemaWithPipe<readonly [v.ArraySchema<v.StrictObjectSchema<{
|
|
482
|
+
readonly type: v.SchemaWithPipe<readonly [v.OptionalSchema<v.PicklistSchema<readonly ["popover"], undefined>, "popover">, v.DescriptionAction<"popover", string>]>;
|
|
449
483
|
readonly line: v.SchemaWithPipe<readonly [v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>]>, v.DescriptionAction<number, string>]>;
|
|
450
484
|
readonly match: v.SchemaWithPipe<readonly [v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, v.DescriptionAction<string, string>]>;
|
|
451
485
|
readonly occurrence: v.SchemaWithPipe<readonly [v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>]>, 1>, v.DescriptionAction<number, string>]>;
|
|
@@ -492,6 +526,7 @@ declare const SpecSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
|
492
526
|
background?: string | undefined;
|
|
493
527
|
}[], string>]>;
|
|
494
528
|
}, undefined>, undefined>, v.MaxLengthAction<{
|
|
529
|
+
type: "popover";
|
|
495
530
|
line: number;
|
|
496
531
|
match: string;
|
|
497
532
|
occurrence: number;
|
|
@@ -510,6 +545,7 @@ declare const SpecSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
|
510
545
|
background?: string | undefined;
|
|
511
546
|
}[];
|
|
512
547
|
}[], number, (issue: v.MaxLengthIssue<{
|
|
548
|
+
type: "popover";
|
|
513
549
|
line: number;
|
|
514
550
|
match: string;
|
|
515
551
|
occurrence: number;
|
|
@@ -528,6 +564,7 @@ declare const SpecSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
|
528
564
|
background?: string | undefined;
|
|
529
565
|
}[];
|
|
530
566
|
}[], number>) => string>]>, readonly []>, v.DescriptionAction<{
|
|
567
|
+
type: "popover";
|
|
531
568
|
line: number;
|
|
532
569
|
match: string;
|
|
533
570
|
occurrence: number;
|
|
@@ -591,6 +628,7 @@ declare const SpecSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
|
591
628
|
icon: string;
|
|
592
629
|
lineNumbers?: boolean | undefined;
|
|
593
630
|
annotations: {
|
|
631
|
+
type: "popover";
|
|
594
632
|
line: number;
|
|
595
633
|
match: string;
|
|
596
634
|
occurrence: number;
|
|
@@ -626,6 +664,7 @@ declare const SpecSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
|
626
664
|
icon: string;
|
|
627
665
|
lineNumbers?: boolean | undefined;
|
|
628
666
|
annotations: {
|
|
667
|
+
type: "popover";
|
|
629
668
|
line: number;
|
|
630
669
|
match: string;
|
|
631
670
|
occurrence: number;
|
|
@@ -661,6 +700,7 @@ declare const SpecSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
|
661
700
|
icon: string;
|
|
662
701
|
lineNumbers?: boolean | undefined;
|
|
663
702
|
annotations: {
|
|
703
|
+
type: "popover";
|
|
664
704
|
line: number;
|
|
665
705
|
match: string;
|
|
666
706
|
occurrence: number;
|
|
@@ -696,6 +736,7 @@ declare const SpecSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
|
696
736
|
icon: string;
|
|
697
737
|
lineNumbers?: boolean | undefined;
|
|
698
738
|
annotations: {
|
|
739
|
+
type: "popover";
|
|
699
740
|
line: number;
|
|
700
741
|
match: string;
|
|
701
742
|
occurrence: number;
|
|
@@ -743,6 +784,7 @@ declare const SpecSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
|
743
784
|
icon: string;
|
|
744
785
|
lineNumbers?: boolean | undefined;
|
|
745
786
|
annotations: {
|
|
787
|
+
type: "popover";
|
|
746
788
|
line: number;
|
|
747
789
|
match: string;
|
|
748
790
|
occurrence: number;
|
|
@@ -790,6 +832,7 @@ declare const SpecSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
|
790
832
|
icon: string;
|
|
791
833
|
lineNumbers?: boolean | undefined;
|
|
792
834
|
annotations: {
|
|
835
|
+
type: "popover";
|
|
793
836
|
line: number;
|
|
794
837
|
match: string;
|
|
795
838
|
occurrence: number;
|
|
@@ -837,6 +880,7 @@ declare const SpecSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
|
837
880
|
icon: string;
|
|
838
881
|
lineNumbers?: boolean | undefined;
|
|
839
882
|
annotations: {
|
|
883
|
+
type: "popover";
|
|
840
884
|
line: number;
|
|
841
885
|
match: string;
|
|
842
886
|
occurrence: number;
|
|
@@ -884,6 +928,7 @@ declare const SpecSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
|
884
928
|
icon: string;
|
|
885
929
|
lineNumbers?: boolean | undefined;
|
|
886
930
|
annotations: {
|
|
931
|
+
type: "popover";
|
|
887
932
|
line: number;
|
|
888
933
|
match: string;
|
|
889
934
|
occurrence: number;
|
|
@@ -946,6 +991,7 @@ declare const SpecSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
|
946
991
|
icon: string;
|
|
947
992
|
lineNumbers?: boolean | undefined;
|
|
948
993
|
annotations: {
|
|
994
|
+
type: "popover";
|
|
949
995
|
line: number;
|
|
950
996
|
match: string;
|
|
951
997
|
occurrence: number;
|
|
@@ -1008,6 +1054,7 @@ declare const SpecSchema: v.SchemaWithPipe<readonly [v.StrictObjectSchema<{
|
|
|
1008
1054
|
icon: string;
|
|
1009
1055
|
lineNumbers?: boolean | undefined;
|
|
1010
1056
|
annotations: {
|
|
1057
|
+
type: "popover";
|
|
1011
1058
|
line: number;
|
|
1012
1059
|
match: string;
|
|
1013
1060
|
occurrence: number;
|
|
@@ -1055,9 +1102,9 @@ declare class SpecError extends Error {
|
|
|
1055
1102
|
constructor(issues: Array<string>);
|
|
1056
1103
|
}
|
|
1057
1104
|
/**
|
|
1058
|
-
* Validates a spec and applies the defaults. It first
|
|
1059
|
-
* validates the spec with the schema of its version and changes it
|
|
1060
|
-
* a SpecError that lists every issue.
|
|
1105
|
+
* Validates a spec and applies the defaults. It first removes the keys of other tools and changes
|
|
1106
|
+
* the fields of older specs, then validates the spec with the schema of its version and changes it
|
|
1107
|
+
* to the latest version. Throws a SpecError that lists every issue.
|
|
1061
1108
|
*/
|
|
1062
1109
|
declare function parseSpec(input: unknown): Spec;
|
|
1063
1110
|
/** Expands highlight entries such as [2, "4-6"] to a set of line numbers, up to the last line */
|
|
@@ -1073,4 +1120,4 @@ type SvgOptions = {
|
|
|
1073
1120
|
};
|
|
1074
1121
|
/** Writes the scene as a standalone SVG document */
|
|
1075
1122
|
declare function toSvg(scene: Scene, options?: SvgOptions): string;
|
|
1076
|
-
export {
|
|
1123
|
+
export { highlight as $, SVG_NAMESPACE as A, CODE_FONTS as At, Scene as B, cssFontFamily as Bt, migrateLegacySpec as C, AUTO_ICON as Ct, METADATA_BY_DEFAULT as D, NO_ICON as Dt, ImageMetadata as E, Icon as Et, LINE_HEIGHT as F, FontFace as Ft, cellCount as G, TextMetrics as H, getFace as Ht, LayoutOptions as I, FontLoader as It, MAC_HEADER as J, layout as K, POPOVER as L, FontMetrics as Lt, createImageMetadata as M, CodeFont as Mt, readImageSpec as N, DEFAULT_CODE_FONT as Nt, PNG_KEYWORDS as O, getIcon as Ot, FONT_SIZE as P, Font as Pt, Token as Q, PaneBox as R, UI_FONT as Rt, IMPLICIT_SPEC_VERSION as S, specJsonSchema as St, stampSpecVersion as T, ICON_IDS as Tt, WINDOW_RADIUS as U, measureUiText as Ut, TAB_HEADER as V, getCodeFont as Vt, WindowBox as W, LANGUAGE_IDS as X, HighlightedLine as Y, PLAIN_TEXT as Z, initPng as _, decodeSpec as _t, MARK_STYLES as a, Mode as at, svgToPng as b, SPEC_SCHEMA_URL as bt, SPEC_LIMITS as c, THEME_IDS as ct, SpecInput as d, getSyntax as dt, inferLanguage as et, SpecSchema as f, getTheme as ft, PngOptions as g, createLink as gt, parseSpec as h, DecodeOptions as ht, LATEST_SPEC_VERSION as i, FrameColors as it, addPngMetadata as j, CODE_FONT_IDS as jt, SOFTWARE as k, resolveIcon as kt, Spec as l, Theme as lt, expandLineRanges as m, DEFAULT_APP_URL as mt, toSvg as n, resolveLanguage as nt, MarkStyle as o, Syntax as ot, WindowSpec as p, resolveMode as pt, normalizeCode as q, ANNOTATION_TYPES as r, DEFAULT_THEME as rt, PaneSpec as s, THEMES as st, SvgOptions as t, isKnownLanguage as tt, SpecError as u, getFrameColors as ut, loadSceneFonts as v, encodeSpec as vt, schemaVersion as w, FILE_ICON as wt, EXTENSION_PREFIX as x, isSpecSchemaUrl as xt, sceneFontFiles as y, readLink as yt, Rect as z, UiFont as zt };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
export { AUTO_ICON, CODE_FONTS, CODE_FONT_IDS, CodeFont, DEFAULT_APP_URL, DEFAULT_CODE_FONT, DEFAULT_THEME, FILE_ICON, FONT_SIZE, Font, FontFace, FontLoader, FontMetrics, FrameColors, type HighlightedLine, ICON_IDS, IMPLICIT_SPEC_VERSION, type Icon, type ImageMetadata, LANGUAGE_IDS, LATEST_SPEC_VERSION, LINE_HEIGHT, type LayoutOptions, MAC_HEADER, MARK_STYLES, METADATA_BY_DEFAULT, type MarkStyle, Mode, NO_ICON, PLAIN_TEXT, PNG_KEYWORDS, POPOVER, type PaneBox, type PaneSpec, type PngOptions, type Rect, SOFTWARE, SPEC_LIMITS, SPEC_SCHEMA_URL, SVG_NAMESPACE, type Scene, type Spec, SpecError, type SpecInput, SpecSchema, type SvgOptions, Syntax, TAB_HEADER, THEMES, THEME_IDS, type TextMetrics, Theme, type Token, UI_FONT, UiFont, WINDOW_RADIUS, type WindowBox, type WindowSpec, addPngMetadata, cellCount, createImageMetadata, createLink, cssFontFamily, decodeSpec, encodeSpec, expandLineRanges, getCodeFont, getFace, getFrameColors, getIcon, getSyntax, getTheme, highlight, inferLanguage, initPng, isKnownLanguage, layout, loadSceneFonts, measureUiText, migrateLegacySpec, normalizeCode, parseSpec, readImageSpec, readLink, resolveIcon, resolveLanguage, resolveMode, sceneFontFiles, specJsonSchema, stampSpecVersion, svgToPng, toSvg };
|
|
1
|
+
import { $ as highlight, A as SVG_NAMESPACE, At as CODE_FONTS, B as Scene, Bt as cssFontFamily, C as migrateLegacySpec, Ct as AUTO_ICON, D as METADATA_BY_DEFAULT, Dt as NO_ICON, E as ImageMetadata, Et as Icon, F as LINE_HEIGHT, Ft as FontFace, G as cellCount, H as TextMetrics, Ht as getFace, I as LayoutOptions, It as FontLoader, J as MAC_HEADER, K as layout, L as POPOVER, Lt as FontMetrics, M as createImageMetadata, Mt as CodeFont, N as readImageSpec, Nt as DEFAULT_CODE_FONT, O as PNG_KEYWORDS, Ot as getIcon, P as FONT_SIZE, Pt as Font, Q as Token, R as PaneBox, Rt as UI_FONT, S as IMPLICIT_SPEC_VERSION, St as specJsonSchema, T as stampSpecVersion, Tt as ICON_IDS, U as WINDOW_RADIUS, Ut as measureUiText, V as TAB_HEADER, Vt as getCodeFont, W as WindowBox, X as LANGUAGE_IDS, Y as HighlightedLine, Z as PLAIN_TEXT, _ as initPng, _t as decodeSpec, a as MARK_STYLES, at as Mode, b as svgToPng, bt as SPEC_SCHEMA_URL, c as SPEC_LIMITS, ct as THEME_IDS, d as SpecInput, dt as getSyntax, et as inferLanguage, f as SpecSchema, ft as getTheme, g as PngOptions, gt as createLink, h as parseSpec, ht as DecodeOptions, i as LATEST_SPEC_VERSION, it as FrameColors, j as addPngMetadata, jt as CODE_FONT_IDS, k as SOFTWARE, kt as resolveIcon, l as Spec, lt as Theme, m as expandLineRanges, mt as DEFAULT_APP_URL, n as toSvg, nt as resolveLanguage, o as MarkStyle, ot as Syntax, p as WindowSpec, pt as resolveMode, q as normalizeCode, r as ANNOTATION_TYPES, rt as DEFAULT_THEME, s as PaneSpec, st as THEMES, t as SvgOptions, tt as isKnownLanguage, u as SpecError, ut as getFrameColors, v as loadSceneFonts, vt as encodeSpec, w as schemaVersion, wt as FILE_ICON, x as EXTENSION_PREFIX, xt as isSpecSchemaUrl, y as sceneFontFiles, yt as readLink, z as Rect, zt as UiFont } from "./index-blYEvwPp.mjs";
|
|
2
|
+
export { ANNOTATION_TYPES, AUTO_ICON, CODE_FONTS, CODE_FONT_IDS, CodeFont, DEFAULT_APP_URL, DEFAULT_CODE_FONT, DEFAULT_THEME, type DecodeOptions, EXTENSION_PREFIX, FILE_ICON, FONT_SIZE, Font, FontFace, FontLoader, FontMetrics, FrameColors, type HighlightedLine, ICON_IDS, IMPLICIT_SPEC_VERSION, type Icon, type ImageMetadata, LANGUAGE_IDS, LATEST_SPEC_VERSION, LINE_HEIGHT, type LayoutOptions, MAC_HEADER, MARK_STYLES, METADATA_BY_DEFAULT, type MarkStyle, Mode, NO_ICON, PLAIN_TEXT, PNG_KEYWORDS, POPOVER, type PaneBox, type PaneSpec, type PngOptions, type Rect, SOFTWARE, SPEC_LIMITS, SPEC_SCHEMA_URL, SVG_NAMESPACE, type Scene, type Spec, SpecError, type SpecInput, SpecSchema, type SvgOptions, Syntax, TAB_HEADER, THEMES, THEME_IDS, type TextMetrics, Theme, type Token, UI_FONT, UiFont, WINDOW_RADIUS, type WindowBox, type WindowSpec, addPngMetadata, cellCount, createImageMetadata, createLink, cssFontFamily, decodeSpec, encodeSpec, expandLineRanges, getCodeFont, getFace, getFrameColors, getIcon, getSyntax, getTheme, highlight, inferLanguage, initPng, isKnownLanguage, isSpecSchemaUrl, layout, loadSceneFonts, measureUiText, migrateLegacySpec, normalizeCode, parseSpec, readImageSpec, readLink, resolveIcon, resolveLanguage, resolveMode, sceneFontFiles, schemaVersion, specJsonSchema, stampSpecVersion, svgToPng, toSvg };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { T as ANNOTATION_TYPES, nt as AUTO_ICON, ct as CODE_FONTS, lt as CODE_FONT_IDS, K as DEFAULT_APP_URL, ut as DEFAULT_CODE_FONT, N as DEFAULT_THEME, Z as EXTENSION_PREFIX, rt as FILE_ICON, p as FONT_SIZE, it as ICON_IDS, Q as IMPLICIT_SPEC_VERSION, B as LANGUAGE_IDS, E as LATEST_SPEC_VERSION, m as LINE_HEIGHT, x as MAC_HEADER, D as MARK_STYLES, o as METADATA_BY_DEFAULT, at as NO_ICON, V as PLAIN_TEXT, s as PNG_KEYWORDS, h as POPOVER, c as SOFTWARE, O as SPEC_LIMITS, S as SPEC_SCHEMA_URL, l as SVG_NAMESPACE, k as SpecError, A as SpecSchema, g as TAB_HEADER, P as THEMES, F as THEME_IDS, dt as UI_FONT, _ as WINDOW_RADIUS, u as addPngMetadata, v as cellCount, d as createImageMetadata, q as createLink, ft as cssFontFamily, J as decodeSpec, Y as encodeSpec, j as expandLineRanges, pt as getCodeFont, mt as getFace, I as getFrameColors, ot as getIcon, L as getSyntax, R as getTheme, H as highlight, U as inferLanguage, n as initPng, W as isKnownLanguage, C as isSpecSchemaUrl, y as layout, r as loadSceneFonts, ht as measureUiText, $ as migrateLegacySpec, b as normalizeCode, M as parseSpec, f as readImageSpec, X as readLink, st as resolveIcon, G as resolveLanguage, z as resolveMode, i as sceneFontFiles, et as schemaVersion, w as specJsonSchema, tt as stampSpecVersion, a as svgToPng, t as toSvg } from "./src-VhDXsdB0.mjs";
|
package/dist/node.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as highlight, A as SVG_NAMESPACE, At as CODE_FONTS, B as Scene, Bt as cssFontFamily, C as migrateLegacySpec, Ct as AUTO_ICON, D as METADATA_BY_DEFAULT, Dt as NO_ICON, E as ImageMetadata, Et as Icon, F as LINE_HEIGHT, Ft as FontFace, G as cellCount, H as TextMetrics, Ht as getFace, I as LayoutOptions, It as FontLoader, J as MAC_HEADER, K as layout, L as POPOVER, Lt as FontMetrics, M as createImageMetadata, Mt as CodeFont, N as readImageSpec, Nt as DEFAULT_CODE_FONT, O as PNG_KEYWORDS, Ot as getIcon, P as FONT_SIZE, Pt as Font, Q as Token, R as PaneBox, Rt as UI_FONT, S as IMPLICIT_SPEC_VERSION, St as specJsonSchema, T as stampSpecVersion, Tt as ICON_IDS, U as WINDOW_RADIUS, Ut as measureUiText, V as TAB_HEADER, Vt as getCodeFont, W as WindowBox, X as LANGUAGE_IDS, Y as HighlightedLine, Z as PLAIN_TEXT, _ as initPng, _t as decodeSpec, a as MARK_STYLES, at as Mode, b as svgToPng, bt as SPEC_SCHEMA_URL, c as SPEC_LIMITS, ct as THEME_IDS, d as SpecInput, dt as getSyntax, et as inferLanguage, f as SpecSchema, ft as getTheme, g as PngOptions, gt as createLink, h as parseSpec, ht as DecodeOptions, i as LATEST_SPEC_VERSION, it as FrameColors, j as addPngMetadata, jt as CODE_FONT_IDS, k as SOFTWARE, kt as resolveIcon, l as Spec, lt as Theme, m as expandLineRanges, mt as DEFAULT_APP_URL, n as toSvg, nt as resolveLanguage, o as MarkStyle, ot as Syntax, p as WindowSpec, pt as resolveMode, q as normalizeCode, r as ANNOTATION_TYPES, rt as DEFAULT_THEME, s as PaneSpec, st as THEMES, t as SvgOptions, tt as isKnownLanguage, u as SpecError, ut as getFrameColors, v as loadSceneFonts, vt as encodeSpec, w as schemaVersion, wt as FILE_ICON, x as EXTENSION_PREFIX, xt as isSpecSchemaUrl, y as sceneFontFiles, yt as readLink, z as Rect, zt as UiFont } from "./index-blYEvwPp.mjs";
|
|
2
2
|
export type RenderFormat = "png" | "svg";
|
|
3
3
|
export type RenderOptions = {
|
|
4
4
|
format?: RenderFormat;
|
|
@@ -18,4 +18,4 @@ export declare const loadFont: FontLoader;
|
|
|
18
18
|
export declare function render(spec: unknown, options?: RenderOptions): Promise<Uint8Array>;
|
|
19
19
|
/** Renders the scene of a layout, so that a caller that needs the scene lays out only once */
|
|
20
20
|
export declare function renderScene(scene: Scene, options?: RenderSceneOptions): Promise<Uint8Array>;
|
|
21
|
-
export { AUTO_ICON, CODE_FONTS, CODE_FONT_IDS, CodeFont, DEFAULT_APP_URL, DEFAULT_CODE_FONT, DEFAULT_THEME, FILE_ICON, FONT_SIZE, Font, FontFace, FontLoader, FontMetrics, FrameColors, type HighlightedLine, ICON_IDS, IMPLICIT_SPEC_VERSION, type Icon, type ImageMetadata, LANGUAGE_IDS, LATEST_SPEC_VERSION, LINE_HEIGHT, type LayoutOptions, MAC_HEADER, MARK_STYLES, METADATA_BY_DEFAULT, type MarkStyle, Mode, NO_ICON, PLAIN_TEXT, PNG_KEYWORDS, POPOVER, type PaneBox, type PaneSpec, type PngOptions, type Rect, SOFTWARE, SPEC_LIMITS, SPEC_SCHEMA_URL, SVG_NAMESPACE, type Scene, type Spec, SpecError, type SpecInput, SpecSchema, type SvgOptions, Syntax, TAB_HEADER, THEMES, THEME_IDS, type TextMetrics, Theme, type Token, UI_FONT, UiFont, WINDOW_RADIUS, type WindowBox, type WindowSpec, addPngMetadata, cellCount, createImageMetadata, createLink, cssFontFamily, decodeSpec, encodeSpec, expandLineRanges, getCodeFont, getFace, getFrameColors, getIcon, getSyntax, getTheme, highlight, inferLanguage, initPng, isKnownLanguage, layout, loadSceneFonts, measureUiText, migrateLegacySpec, normalizeCode, parseSpec, readImageSpec, readLink, resolveIcon, resolveLanguage, resolveMode, sceneFontFiles, specJsonSchema, stampSpecVersion, svgToPng, toSvg };
|
|
21
|
+
export { ANNOTATION_TYPES, AUTO_ICON, CODE_FONTS, CODE_FONT_IDS, CodeFont, DEFAULT_APP_URL, DEFAULT_CODE_FONT, DEFAULT_THEME, type DecodeOptions, EXTENSION_PREFIX, FILE_ICON, FONT_SIZE, Font, FontFace, FontLoader, FontMetrics, FrameColors, type HighlightedLine, ICON_IDS, IMPLICIT_SPEC_VERSION, type Icon, type ImageMetadata, LANGUAGE_IDS, LATEST_SPEC_VERSION, LINE_HEIGHT, type LayoutOptions, MAC_HEADER, MARK_STYLES, METADATA_BY_DEFAULT, type MarkStyle, Mode, NO_ICON, PLAIN_TEXT, PNG_KEYWORDS, POPOVER, type PaneBox, type PaneSpec, type PngOptions, type Rect, SOFTWARE, SPEC_LIMITS, SPEC_SCHEMA_URL, SVG_NAMESPACE, type Scene, type Spec, SpecError, type SpecInput, SpecSchema, type SvgOptions, Syntax, TAB_HEADER, THEMES, THEME_IDS, type TextMetrics, Theme, type Token, UI_FONT, UiFont, WINDOW_RADIUS, type WindowBox, type WindowSpec, addPngMetadata, cellCount, createImageMetadata, createLink, cssFontFamily, decodeSpec, encodeSpec, expandLineRanges, getCodeFont, getFace, getFrameColors, getIcon, getSyntax, getTheme, highlight, inferLanguage, initPng, isKnownLanguage, isSpecSchemaUrl, layout, loadSceneFonts, measureUiText, migrateLegacySpec, normalizeCode, parseSpec, readImageSpec, readLink, resolveIcon, resolveLanguage, resolveMode, sceneFontFiles, schemaVersion, specJsonSchema, stampSpecVersion, svgToPng, toSvg };
|
package/dist/node.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as migrateLegacySpec, A as SpecSchema, B as LANGUAGE_IDS, C as isSpecSchemaUrl, D as MARK_STYLES, E as LATEST_SPEC_VERSION, F as THEME_IDS, G as resolveLanguage, H as highlight, I as getFrameColors, J as decodeSpec, K as DEFAULT_APP_URL, L as getSyntax, M as parseSpec, N as DEFAULT_THEME, O as SPEC_LIMITS, P as THEMES, Q as IMPLICIT_SPEC_VERSION, R as getTheme, S as SPEC_SCHEMA_URL, T as ANNOTATION_TYPES, U as inferLanguage, V as PLAIN_TEXT, W as isKnownLanguage, X as readLink, Y as encodeSpec, Z as EXTENSION_PREFIX, _ as WINDOW_RADIUS, a as svgToPng, at as NO_ICON, b as normalizeCode, c as SOFTWARE, ct as CODE_FONTS, d as createImageMetadata, dt as UI_FONT, et as schemaVersion, f as readImageSpec, ft as cssFontFamily, g as TAB_HEADER, h as POPOVER, ht as measureUiText, i as sceneFontFiles, it as ICON_IDS, j as expandLineRanges, k as SpecError, l as SVG_NAMESPACE, lt as CODE_FONT_IDS, m as LINE_HEIGHT, mt as getFace, n as initPng, nt as AUTO_ICON, o as METADATA_BY_DEFAULT, ot as getIcon, p as FONT_SIZE, pt as getCodeFont, q as createLink, r as loadSceneFonts, rt as FILE_ICON, s as PNG_KEYWORDS, st as resolveIcon, t as toSvg, tt as stampSpecVersion, u as addPngMetadata, ut as DEFAULT_CODE_FONT, v as cellCount, w as specJsonSchema, x as MAC_HEADER, y as layout, z as resolveMode } from "./src-VhDXsdB0.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
4
|
// src/node.ts
|
|
@@ -28,4 +28,4 @@ async function renderScene(scene, options = {}) {
|
|
|
28
28
|
metadata
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
|
-
export { AUTO_ICON, CODE_FONTS, CODE_FONT_IDS, DEFAULT_APP_URL, DEFAULT_CODE_FONT, DEFAULT_THEME, FILE_ICON, FONT_SIZE, ICON_IDS, IMPLICIT_SPEC_VERSION, LANGUAGE_IDS, LATEST_SPEC_VERSION, LINE_HEIGHT, MAC_HEADER, MARK_STYLES, METADATA_BY_DEFAULT, NO_ICON, PLAIN_TEXT, PNG_KEYWORDS, POPOVER, SOFTWARE, SPEC_LIMITS, SPEC_SCHEMA_URL, SVG_NAMESPACE, SpecError, SpecSchema, TAB_HEADER, THEMES, THEME_IDS, UI_FONT, WINDOW_RADIUS, addPngMetadata, cellCount, createImageMetadata, createLink, cssFontFamily, decodeSpec, encodeSpec, expandLineRanges, getCodeFont, getFace, getFrameColors, getIcon, getSyntax, getTheme, highlight, inferLanguage, initPng, isKnownLanguage, layout, loadFont, loadSceneFonts, measureUiText, migrateLegacySpec, normalizeCode, parseSpec, readImageSpec, readLink, render, renderScene, resolveIcon, resolveLanguage, resolveMode, sceneFontFiles, specJsonSchema, stampSpecVersion, svgToPng, toSvg };
|
|
31
|
+
export { ANNOTATION_TYPES, AUTO_ICON, CODE_FONTS, CODE_FONT_IDS, DEFAULT_APP_URL, DEFAULT_CODE_FONT, DEFAULT_THEME, EXTENSION_PREFIX, FILE_ICON, FONT_SIZE, ICON_IDS, IMPLICIT_SPEC_VERSION, LANGUAGE_IDS, LATEST_SPEC_VERSION, LINE_HEIGHT, MAC_HEADER, MARK_STYLES, METADATA_BY_DEFAULT, NO_ICON, PLAIN_TEXT, PNG_KEYWORDS, POPOVER, SOFTWARE, SPEC_LIMITS, SPEC_SCHEMA_URL, SVG_NAMESPACE, SpecError, SpecSchema, TAB_HEADER, THEMES, THEME_IDS, UI_FONT, WINDOW_RADIUS, addPngMetadata, cellCount, createImageMetadata, createLink, cssFontFamily, decodeSpec, encodeSpec, expandLineRanges, getCodeFont, getFace, getFrameColors, getIcon, getSyntax, getTheme, highlight, inferLanguage, initPng, isKnownLanguage, isSpecSchemaUrl, layout, loadFont, loadSceneFonts, measureUiText, migrateLegacySpec, normalizeCode, parseSpec, readImageSpec, readLink, render, renderScene, resolveIcon, resolveLanguage, resolveMode, sceneFontFiles, schemaVersion, specJsonSchema, stampSpecVersion, svgToPng, toSvg };
|
|
@@ -773,6 +773,29 @@ function base64UrlToBytes(base64url) {
|
|
|
773
773
|
}
|
|
774
774
|
// src/migrate.ts
|
|
775
775
|
const IMPLICIT_SPEC_VERSION = 1;
|
|
776
|
+
const SCHEMA_VERSION = /\/schema\/v(\d+)\.json$/;
|
|
777
|
+
/**
|
|
778
|
+
* The version that the $schema URL of a spec names, or undefined when the spec has no such URL.
|
|
779
|
+
* Only the path counts, so that the URL of each host that serves the schema gives the version.
|
|
780
|
+
*/
|
|
781
|
+
function schemaVersion(spec) {
|
|
782
|
+
if (typeof spec !== "object" || spec === null || !("$schema" in spec)) return void 0;
|
|
783
|
+
const url = spec.$schema;
|
|
784
|
+
if (typeof url !== "string" || !URL.canParse(url)) return void 0;
|
|
785
|
+
const match = SCHEMA_VERSION.exec(new URL(url).pathname);
|
|
786
|
+
return match ? Number(match[1]) : void 0;
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Keys with this prefix hold data of other tools, such as an editor state. The renderer ignores
|
|
790
|
+
* them in each object of a spec, so that a spec with such keys stays valid in all versions.
|
|
791
|
+
*/
|
|
792
|
+
const EXTENSION_PREFIX = "x-";
|
|
793
|
+
/** Removes the keys of other tools from all objects of a spec */
|
|
794
|
+
function withoutExtensions(value) {
|
|
795
|
+
if (Array.isArray(value)) return value.map(withoutExtensions);
|
|
796
|
+
if (typeof value !== "object" || value === null) return value;
|
|
797
|
+
return Object.fromEntries(Object.entries(value).filter(([key]) => !key.startsWith("x-")).map(([key, entry]) => [key, withoutExtensions(entry)]));
|
|
798
|
+
}
|
|
776
799
|
const CHROME_FIELDS = {
|
|
777
800
|
mac: {},
|
|
778
801
|
tabs: {
|
|
@@ -806,12 +829,13 @@ function migrateLegacySpec(spec) {
|
|
|
806
829
|
}
|
|
807
830
|
/**
|
|
808
831
|
* Adds the version to a spec without one, so that a stored spec keeps its meaning when a later
|
|
809
|
-
* version changes the format.
|
|
832
|
+
* version changes the format. The version comes from the $schema URL, or is version 1 without it.
|
|
833
|
+
* A spec with a version stays as it is.
|
|
810
834
|
*/
|
|
811
835
|
function stampSpecVersion(spec) {
|
|
812
836
|
if (typeof spec !== "object" || spec === null || Array.isArray(spec) || "v" in spec) return spec;
|
|
813
837
|
return {
|
|
814
|
-
v: 1,
|
|
838
|
+
v: schemaVersion(spec) ?? 1,
|
|
815
839
|
...spec
|
|
816
840
|
};
|
|
817
841
|
}
|
|
@@ -820,22 +844,53 @@ function stampSpecVersion(spec) {
|
|
|
820
844
|
* The hosted web app. The CLI takes another app from its TIN_APP_URL environment variable,
|
|
821
845
|
* for example the local editor during development.
|
|
822
846
|
*/
|
|
823
|
-
const DEFAULT_APP_URL = "https://
|
|
847
|
+
const DEFAULT_APP_URL = "https://tin.so/";
|
|
824
848
|
const HASH_KEY = "spec=";
|
|
825
|
-
async function
|
|
826
|
-
const
|
|
827
|
-
return new Uint8Array(await
|
|
849
|
+
async function compress(bytes) {
|
|
850
|
+
const stream = new Blob([bytes]).stream().pipeThrough(new CompressionStream("deflate-raw"));
|
|
851
|
+
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
852
|
+
}
|
|
853
|
+
/** Inflates data, or returns undefined when the data is bad or larger than the limit */
|
|
854
|
+
async function inflate$1(bytes, format, limit = Number.POSITIVE_INFINITY) {
|
|
855
|
+
const reader = new Blob([bytes]).stream().pipeThrough(new DecompressionStream(format)).getReader();
|
|
856
|
+
const parts = [];
|
|
857
|
+
let size = 0;
|
|
858
|
+
try {
|
|
859
|
+
for (;;) {
|
|
860
|
+
const { done, value } = await reader.read();
|
|
861
|
+
if (done) break;
|
|
862
|
+
size += value.length;
|
|
863
|
+
if (size > limit) {
|
|
864
|
+
await reader.cancel().catch(() => void 0);
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
parts.push(value);
|
|
868
|
+
}
|
|
869
|
+
} catch {
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
const out = new Uint8Array(size);
|
|
873
|
+
let offset = 0;
|
|
874
|
+
for (const part of parts) {
|
|
875
|
+
out.set(part, offset);
|
|
876
|
+
offset += part.length;
|
|
877
|
+
}
|
|
878
|
+
return out;
|
|
828
879
|
}
|
|
829
880
|
/**
|
|
830
881
|
* Compresses a spec into a URL-safe string. A spec without a version gets version 1, so that the
|
|
831
882
|
* link keeps its meaning when a later version changes the format.
|
|
832
883
|
*/
|
|
833
884
|
async function encodeSpec(spec) {
|
|
834
|
-
return bytesToBase64Url(await
|
|
885
|
+
return bytesToBase64Url(await compress(new TextEncoder().encode(JSON.stringify(stampSpecVersion(spec)))));
|
|
835
886
|
}
|
|
836
|
-
/**
|
|
837
|
-
|
|
838
|
-
|
|
887
|
+
/**
|
|
888
|
+
* Reads a spec from a string of the encoder, with the fields of older links changed. Throws for
|
|
889
|
+
* bad data and for a spec larger than the limit.
|
|
890
|
+
*/
|
|
891
|
+
async function decodeSpec(encoded, options = {}) {
|
|
892
|
+
const json = await inflate$1(base64UrlToBytes(encoded), "deflate-raw", options.maxBytes);
|
|
893
|
+
if (!json) throw new Error("The link has bad data or a spec that is too large");
|
|
839
894
|
return migrateLegacySpec(JSON.parse(new TextDecoder().decode(json)));
|
|
840
895
|
}
|
|
841
896
|
/**
|
|
@@ -848,10 +903,10 @@ async function createLink(spec, appUrl = DEFAULT_APP_URL) {
|
|
|
848
903
|
return url.toString();
|
|
849
904
|
}
|
|
850
905
|
/** Reads the spec from the fragment of a link, or returns undefined when the fragment has none */
|
|
851
|
-
async function readLink(link) {
|
|
906
|
+
async function readLink(link, options = {}) {
|
|
852
907
|
const hash = new URL(link).hash.slice(1);
|
|
853
908
|
if (!hash.startsWith(HASH_KEY)) return void 0;
|
|
854
|
-
return decodeSpec(hash.slice(5));
|
|
909
|
+
return decodeSpec(hash.slice(5), options);
|
|
855
910
|
}
|
|
856
911
|
// src/themes/tailwind-dark.json
|
|
857
912
|
var tailwind_dark_default = {
|
|
@@ -1704,7 +1759,43 @@ const EXTENSIONS = {
|
|
|
1704
1759
|
htm: "html",
|
|
1705
1760
|
txt: PLAIN_TEXT,
|
|
1706
1761
|
makefile: "make",
|
|
1707
|
-
gemfile: "ruby"
|
|
1762
|
+
gemfile: "ruby",
|
|
1763
|
+
adb: "ada",
|
|
1764
|
+
as: "actionscript-3",
|
|
1765
|
+
bal: "ballerina",
|
|
1766
|
+
bib: "bibtex",
|
|
1767
|
+
clar: "clarity",
|
|
1768
|
+
cls: "apex",
|
|
1769
|
+
cob: "cobol",
|
|
1770
|
+
cr: "crystal",
|
|
1771
|
+
dm: "dream-maker",
|
|
1772
|
+
do: "stata",
|
|
1773
|
+
el: "emacs-lisp",
|
|
1774
|
+
ex: "elixir",
|
|
1775
|
+
feature: "gherkin",
|
|
1776
|
+
fnl: "fennel",
|
|
1777
|
+
gs: "genie",
|
|
1778
|
+
hx: "haxe",
|
|
1779
|
+
ll: "llvm",
|
|
1780
|
+
ml: "ocaml",
|
|
1781
|
+
mm: "objective-cpp",
|
|
1782
|
+
msg: "rosmsg",
|
|
1783
|
+
nsi: "nsis",
|
|
1784
|
+
pas: "pascal",
|
|
1785
|
+
pp: "puppet",
|
|
1786
|
+
pq: "powerquery",
|
|
1787
|
+
purs: "purescript",
|
|
1788
|
+
rkt: "racket",
|
|
1789
|
+
scm: "scheme",
|
|
1790
|
+
service: "systemd",
|
|
1791
|
+
sol: "solidity",
|
|
1792
|
+
st: "smalltalk",
|
|
1793
|
+
sv: "system-verilog",
|
|
1794
|
+
ttl: "turtle",
|
|
1795
|
+
vhd: "vhdl",
|
|
1796
|
+
wat: "wasm",
|
|
1797
|
+
wy: "wenyan",
|
|
1798
|
+
zs: "zenscript"
|
|
1708
1799
|
};
|
|
1709
1800
|
const TAILWIND_THEMES = {
|
|
1710
1801
|
light: {
|
|
@@ -3407,7 +3498,9 @@ const MarkSchema = v.strictObject({
|
|
|
3407
3498
|
background: describe(v.optional(hexColor()), "Fill behind the text, as a hex color such as \"#ffd60a\" or \"#ffd60a40\" with alpha")
|
|
3408
3499
|
});
|
|
3409
3500
|
const MarkWithEffectSchema = v.pipe(MarkSchema, v.check((mark) => mark.style !== void 0 || mark.color !== void 0 || mark.background !== void 0, "A mark needs a style, a color or a background"));
|
|
3501
|
+
const ANNOTATION_TYPES = ["popover"];
|
|
3410
3502
|
const AnnotationSchema = v.strictObject({
|
|
3503
|
+
type: describe(v.optional(v.picklist(ANNOTATION_TYPES), "popover"), "Kind of the annotation. popover is a card below the text, as the hover card of an editor"),
|
|
3411
3504
|
line: describe(v.pipe(v.number(), v.integer(), v.minValue(1)), "Line of the anchor, from 1"),
|
|
3412
3505
|
match: describe(v.pipe(v.string(), v.minLength(1)), "Text in the line that the popover points to"),
|
|
3413
3506
|
occurrence: describe(v.optional(v.pipe(v.number(), v.integer(), v.minValue(1)), 1), "Which occurrence of the match in the line, from 1"),
|
|
@@ -3451,8 +3544,14 @@ const WindowSchema = v.strictObject({
|
|
|
3451
3544
|
* Versions of the spec format.
|
|
3452
3545
|
*
|
|
3453
3546
|
* - Increase v only for a breaking change: a field that changes its meaning, gets a new name or
|
|
3454
|
-
* goes away
|
|
3455
|
-
*
|
|
3547
|
+
* goes away, a changed default, a value that a list no longer has (a theme, font, icon or
|
|
3548
|
+
* language id), or a smaller limit. Specs leave out the defaults, so a new default changes old
|
|
3549
|
+
* images. A new optional field, a new value or a larger limit does not increase v, because older
|
|
3550
|
+
* specs stay valid.
|
|
3551
|
+
* - The version is v, or the version of the $schema URL (/schema/v<number>.json) without v. When
|
|
3552
|
+
* both are there, they must be the same. A spec without both is version 1, now and in all later
|
|
3553
|
+
* versions, because older specs have neither.
|
|
3554
|
+
* - Keys that start with "x-" are for other tools. The renderer ignores them in every object.
|
|
3456
3555
|
* - For a new version, add its schema (with v as a required literal of the new number) to
|
|
3457
3556
|
* SPEC_SCHEMAS, a function that changes a parsed spec of the version before into the new
|
|
3458
3557
|
* version to MIGRATIONS, and increase LATEST_SPEC_VERSION. The schema of the latest version is
|
|
@@ -3460,8 +3559,8 @@ const WindowSchema = v.strictObject({
|
|
|
3460
3559
|
*/
|
|
3461
3560
|
const LATEST_SPEC_VERSION = 1;
|
|
3462
3561
|
const SpecV1Schema = v.pipe(v.strictObject({
|
|
3463
|
-
$schema: describe(v.optional(v.pipe(v.string(), v.url())), "URL of the JSON Schema of the spec, so that an editor can check the spec and complete its fields. The
|
|
3464
|
-
v: describe(v.optional(v.literal(1), 1), "Version of the spec format. Leave it out or use 1.
|
|
3562
|
+
$schema: describe(v.optional(v.pipe(v.string(), v.url())), "URL of the JSON Schema of the spec, so that an editor can check the spec and complete its fields. Its path names the version, such as /schema/v1.json, which counts as v. The CLI also uses it to tell a spec from other JSON"),
|
|
3563
|
+
v: describe(v.optional(v.literal(1), 1), "Version of the spec format. Leave it out or use 1. Without v, the version of the $schema URL counts, and without both, the version is 1"),
|
|
3465
3564
|
theme: describe(v.optional(v.picklist(THEME_IDS), DEFAULT_THEME), "Theme from ray.so"),
|
|
3466
3565
|
mode: describe(v.optional(v.picklist(["dark", "light"]), "dark"), "Color mode. A theme with only one mode ignores it"),
|
|
3467
3566
|
background: describe(v.optional(v.boolean(), true), "Gradient background. false is transparent"),
|
|
@@ -3495,25 +3594,36 @@ function formatIssue(issue) {
|
|
|
3495
3594
|
const path = v.getDotPath(issue) ?? "(root)";
|
|
3496
3595
|
if (issue.type === "strict_object" && issue.expected === "never") return `${path}: unknown field. Check the name, or update tin.so, because a newer version can support it`;
|
|
3497
3596
|
if (issue.type === "strict_object" && issue.input === void 0) return `${path}: required field missing`;
|
|
3498
|
-
if (issue.type === "picklist" && issue.expected) return `${path}: expected one of ${issue.expected}, received ${issue.received}`;
|
|
3597
|
+
if (issue.type === "picklist" && issue.expected) return `${path}: expected one of ${issue.expected}, received ${issue.received}. Check the value, or update tin.so, because a newer version can support it`;
|
|
3499
3598
|
return `${path}: ${issue.message}`;
|
|
3500
3599
|
}
|
|
3501
|
-
/**
|
|
3600
|
+
/**
|
|
3601
|
+
* The version of a spec input: v, or the version of the $schema URL without v. A spec without
|
|
3602
|
+
* both is version 1.
|
|
3603
|
+
*/
|
|
3502
3604
|
function readVersion(input) {
|
|
3503
|
-
|
|
3504
|
-
const
|
|
3505
|
-
if (
|
|
3506
|
-
|
|
3507
|
-
|
|
3605
|
+
const fromSchema = schemaVersion(input);
|
|
3606
|
+
const field = typeof input === "object" && input !== null && "v" in input ? input.v : void 0;
|
|
3607
|
+
if (field === void 0) {
|
|
3608
|
+
const version = fromSchema ?? 1;
|
|
3609
|
+
if (version < 1) throw new SpecError([`$schema: the URL names version ${version}, but versions start at 1`]);
|
|
3610
|
+
return checkSupported(version, "$schema");
|
|
3611
|
+
}
|
|
3612
|
+
if (typeof field !== "number" || !Number.isInteger(field) || field < 1) throw new SpecError([`v: the version must be a whole number from 1, received ${typeof field === "number" ? field : JSON.stringify(field) ?? typeof field}`]);
|
|
3613
|
+
if (fromSchema !== void 0 && fromSchema !== field) throw new SpecError([`v: the spec has version ${field}, but its $schema URL names version ${fromSchema}. Make them the same, or remove one`]);
|
|
3614
|
+
return checkSupported(field, "v");
|
|
3615
|
+
}
|
|
3616
|
+
function checkSupported(version, path) {
|
|
3617
|
+
if (version > 1) throw new SpecError([`${path}: This spec has version ${version}, but this tin.so supports up to version 1. Update tin.so.`]);
|
|
3508
3618
|
return version;
|
|
3509
3619
|
}
|
|
3510
3620
|
/**
|
|
3511
|
-
* Validates a spec and applies the defaults. It first
|
|
3512
|
-
* validates the spec with the schema of its version and changes it
|
|
3513
|
-
* a SpecError that lists every issue.
|
|
3621
|
+
* Validates a spec and applies the defaults. It first removes the keys of other tools and changes
|
|
3622
|
+
* the fields of older specs, then validates the spec with the schema of its version and changes it
|
|
3623
|
+
* to the latest version. Throws a SpecError that lists every issue.
|
|
3514
3624
|
*/
|
|
3515
3625
|
function parseSpec(input) {
|
|
3516
|
-
const migrated = migrateLegacySpec(input);
|
|
3626
|
+
const migrated = migrateLegacySpec(withoutExtensions(input));
|
|
3517
3627
|
const version = readVersion(migrated);
|
|
3518
3628
|
const result = v.safeParse(SPEC_SCHEMAS[version], migrated);
|
|
3519
3629
|
if (!result.success) throw new SpecError(result.issues.map(formatIssue));
|
|
@@ -3547,9 +3657,32 @@ function specJsonSchema() {
|
|
|
3547
3657
|
$schema,
|
|
3548
3658
|
$id: SPEC_SCHEMA_URL,
|
|
3549
3659
|
title: "tin.so spec",
|
|
3550
|
-
...schema
|
|
3660
|
+
...allowExtensions(schema)
|
|
3551
3661
|
};
|
|
3552
3662
|
}
|
|
3663
|
+
function allowExtensions(node) {
|
|
3664
|
+
if (Array.isArray(node)) return node.map(allowExtensions);
|
|
3665
|
+
if (typeof node !== "object" || node === null) return node;
|
|
3666
|
+
const out = Object.fromEntries(Object.entries(node).map(([key, value]) => [key, allowExtensions(value)]));
|
|
3667
|
+
if (out.type === "object" && out.additionalProperties === false) out.patternProperties = { [`^x-`]: {} };
|
|
3668
|
+
return out;
|
|
3669
|
+
}
|
|
3670
|
+
const SPEC_SCHEMA_HOSTS = ["code-image.zirkelc.workers.dev"];
|
|
3671
|
+
const SPEC_SCHEMA_PATH = /\/schema\/v\d+\.json$/;
|
|
3672
|
+
/**
|
|
3673
|
+
* True when the value is the URL of a tin.so spec schema: a path that ends with
|
|
3674
|
+
* /schema/v<number>.json on the host of the hosted app, on an earlier host of the app, or on the
|
|
3675
|
+
* host of the given app URL (for example a local editor).
|
|
3676
|
+
*/
|
|
3677
|
+
function isSpecSchemaUrl(value, appUrl) {
|
|
3678
|
+
if (typeof value !== "string" || !URL.canParse(value)) return false;
|
|
3679
|
+
const url = new URL(value);
|
|
3680
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") return false;
|
|
3681
|
+
if (!SPEC_SCHEMA_PATH.test(url.pathname)) return false;
|
|
3682
|
+
const hosts = [new URL(SPEC_SCHEMA_URL).host, ...SPEC_SCHEMA_HOSTS];
|
|
3683
|
+
if (appUrl !== void 0 && URL.canParse(appUrl)) hosts.push(new URL(appUrl).host);
|
|
3684
|
+
return hosts.includes(url.host);
|
|
3685
|
+
}
|
|
3553
3686
|
// src/sizes.ts
|
|
3554
3687
|
const MAC_HEADER = {
|
|
3555
3688
|
paddingTop: 10,
|
|
@@ -3590,7 +3723,7 @@ function line(x1, y1, x2, y2, extra) {
|
|
|
3590
3723
|
...extra
|
|
3591
3724
|
})}/>`;
|
|
3592
3725
|
}
|
|
3593
|
-
function inflate
|
|
3726
|
+
function inflate(rect, by) {
|
|
3594
3727
|
return {
|
|
3595
3728
|
x: rect.x - by,
|
|
3596
3729
|
y: rect.y - by,
|
|
@@ -4036,7 +4169,7 @@ function boxShadow(context, window, layers) {
|
|
|
4036
4169
|
})}><rect width="${width}" height="${height}" fill="white"/>${roundedRect(window, window.radius, { fill: "black" })}</mask></defs>`, `<g mask="url(#${mask})">`];
|
|
4037
4170
|
for (const layer of layers) {
|
|
4038
4171
|
const spread = layer.spread ?? 0;
|
|
4039
|
-
const box = inflate
|
|
4172
|
+
const box = inflate({
|
|
4040
4173
|
...window,
|
|
4041
4174
|
y: window.y + layer.y
|
|
4042
4175
|
}, spread);
|
|
@@ -4134,11 +4267,11 @@ const DEFAULT_FRAME = {
|
|
|
4134
4267
|
behind: (context, window) => window.frame ? boxShadow(context, window, RAYSO_SHADOW) : "",
|
|
4135
4268
|
body: ({ scene }, window) => roundedRect(window, window.radius, { fill: scene.frame.background }),
|
|
4136
4269
|
header: (context, window) => windowControls(window, context.scene.frame.control) + titleBarText(context, window, { title: context.scene.frame.title }),
|
|
4137
|
-
border: ({ scene }, window) => roundedRect(inflate
|
|
4270
|
+
border: ({ scene }, window) => roundedRect(inflate(window, .5), window.radius + .5, {
|
|
4138
4271
|
fill: "none",
|
|
4139
4272
|
stroke: scene.frame.highlightBorder,
|
|
4140
4273
|
"stroke-width": 1
|
|
4141
|
-
}) + roundedRect(inflate
|
|
4274
|
+
}) + roundedRect(inflate(window, 1.25), window.radius + 1.25, {
|
|
4142
4275
|
fill: "none",
|
|
4143
4276
|
stroke: scene.frame.shadowBorder,
|
|
4144
4277
|
"stroke-width": .5
|
|
@@ -4319,11 +4452,11 @@ const frame$20 = {
|
|
|
4319
4452
|
x2: 0,
|
|
4320
4453
|
y2: inner.y + inner.height * .15
|
|
4321
4454
|
})}><stop offset="0" stop-color="${style.shine[0]}"/><stop offset="1" stop-color="${style.shine[1]}"/></linearGradient>`}</defs>`, roundedRect(window, window.radius, { fill: style.shell })];
|
|
4322
|
-
if (context.mode === "light") out.push(roundedRect(inflate
|
|
4455
|
+
if (context.mode === "light") out.push(roundedRect(inflate(window, .5), window.radius + .5, {
|
|
4323
4456
|
fill: "none",
|
|
4324
4457
|
stroke: style.ring
|
|
4325
4458
|
}));
|
|
4326
|
-
out.push(roundedRect(inner, 10, { fill: style.window }), roundedRect(inner, 10, { fill: `url(#${shine})` }), roundedRect(inflate
|
|
4459
|
+
out.push(roundedRect(inner, 10, { fill: style.window }), roundedRect(inner, 10, { fill: `url(#${shine})` }), roundedRect(inflate(inner, .5), 10.5, {
|
|
4327
4460
|
fill: "none",
|
|
4328
4461
|
stroke: style.ring
|
|
4329
4462
|
}));
|
|
@@ -4556,7 +4689,7 @@ const frame$18 = {
|
|
|
4556
4689
|
behind: (context, window) => {
|
|
4557
4690
|
if (window.frame) return DEFAULT_FRAME.behind(context, window);
|
|
4558
4691
|
const id = context.id(`haze-${window.x}-${window.y}`);
|
|
4559
|
-
return `<defs>${blurFilter(id, 12)}</defs>${roundedRect(inflate
|
|
4692
|
+
return `<defs>${blurFilter(id, 12)}</defs>${roundedRect(inflate(window, 8), 24, {
|
|
4560
4693
|
fill: context.scene.frame.background,
|
|
4561
4694
|
filter: `url(#${id})`
|
|
4562
4695
|
})}`;
|
|
@@ -4635,7 +4768,7 @@ const frame$17 = {
|
|
|
4635
4768
|
return out.join("");
|
|
4636
4769
|
},
|
|
4637
4770
|
behind: () => "",
|
|
4638
|
-
border: ({ mode }, window) => roundedRect(inflate
|
|
4771
|
+
border: ({ mode }, window) => roundedRect(inflate(window, -.5), 0, {
|
|
4639
4772
|
fill: "none",
|
|
4640
4773
|
stroke: COLORS$14[mode].border
|
|
4641
4774
|
})
|
|
@@ -5114,7 +5247,7 @@ function diagonals(window, stroke) {
|
|
|
5114
5247
|
}
|
|
5115
5248
|
/** The thin rounded outline of the window */
|
|
5116
5249
|
function outline(window, stroke) {
|
|
5117
|
-
return roundedRect(inflate
|
|
5250
|
+
return roundedRect(inflate(window, -.5), 23.5, {
|
|
5118
5251
|
fill: "none",
|
|
5119
5252
|
stroke
|
|
5120
5253
|
});
|
|
@@ -5901,7 +6034,7 @@ const frame$9 = {
|
|
|
5901
6034
|
behind: (context, window) => {
|
|
5902
6035
|
if (window.frame) return DEFAULT_FRAME.behind(context, window);
|
|
5903
6036
|
const id = context.id(`haze-${window.x}-${window.y}`);
|
|
5904
|
-
return `<defs>${blurFilter(id, 12)}</defs>${roundedRect(inflate
|
|
6037
|
+
return `<defs>${blurFilter(id, 12)}</defs>${roundedRect(inflate(window, 8), 24, {
|
|
5905
6038
|
fill: context.scene.frame.background,
|
|
5906
6039
|
filter: `url(#${id})`
|
|
5907
6040
|
})}`;
|
|
@@ -6032,7 +6165,7 @@ function glows(context, window) {
|
|
|
6032
6165
|
function rings(context, window, from) {
|
|
6033
6166
|
const out = [];
|
|
6034
6167
|
RINGS.slice(from).forEach((ring, index) => {
|
|
6035
|
-
const box = inflate
|
|
6168
|
+
const box = inflate(window, ring.offset);
|
|
6036
6169
|
const id = context.id(`ring-${index + from}-${window.x}-${window.y}`);
|
|
6037
6170
|
const [a, b, c, d] = ring.stops;
|
|
6038
6171
|
out.push(`<defs>${linearGradient(id, 135, box, [
|
|
@@ -6042,7 +6175,7 @@ function rings(context, window, from) {
|
|
|
6042
6175
|
[c, "rgba(0, 220, 130, 0)"],
|
|
6043
6176
|
[d, GREEN],
|
|
6044
6177
|
[1, GREEN]
|
|
6045
|
-
])}</defs>`, roundedRect(inflate
|
|
6178
|
+
])}</defs>`, roundedRect(inflate(box, -.5), window.radius + ring.radius - .5, {
|
|
6046
6179
|
fill: "none",
|
|
6047
6180
|
stroke: `url(#${id})`,
|
|
6048
6181
|
"stroke-opacity": ring.opacity
|
|
@@ -6200,7 +6333,7 @@ const frame$7 = {
|
|
|
6200
6333
|
return `<defs>${linearGradient(id, 238, box, [[0, from], [1, to]])}</defs><rect width="${scene.width}" height="${scene.height}" fill="url(#${id})"/>`;
|
|
6201
6334
|
},
|
|
6202
6335
|
behind: (context, window) => window.frame ? boxShadow(context, window, shadow(context.mode)) : "",
|
|
6203
|
-
border: ({ mode }, window) => roundedRect(inflate
|
|
6336
|
+
border: ({ mode }, window) => roundedRect(inflate(window, -.25), window.radius - .25, {
|
|
6204
6337
|
fill: "none",
|
|
6205
6338
|
stroke: COLORS$5[mode].border,
|
|
6206
6339
|
"stroke-width": .5
|
|
@@ -6399,19 +6532,19 @@ const frame$6 = {
|
|
|
6399
6532
|
border: (context, window) => {
|
|
6400
6533
|
const style = COLORS$4[context.mode];
|
|
6401
6534
|
const r = window.radius;
|
|
6402
|
-
const inner = inflate
|
|
6535
|
+
const inner = inflate(window, -1);
|
|
6403
6536
|
const mask = context.id(`highlight-${window.x}-${window.y}`);
|
|
6404
6537
|
return `<defs><mask ${attrs({
|
|
6405
6538
|
id: mask,
|
|
6406
6539
|
maskUnits: "userSpaceOnUse",
|
|
6407
|
-
...inflate
|
|
6408
|
-
})}>${roundedRect(inflate
|
|
6540
|
+
...inflate(window, 2)
|
|
6541
|
+
})}>${roundedRect(inflate(window, 2), 0, { fill: "white" })}${roundedRect({
|
|
6409
6542
|
...inner,
|
|
6410
6543
|
y: inner.y + 1
|
|
6411
6544
|
}, r - 1, { fill: "black" })}</mask></defs>${roundedRect(inner, r - 1, {
|
|
6412
6545
|
fill: style.highlight,
|
|
6413
6546
|
mask: `url(#${mask})`
|
|
6414
|
-
})}` + roundedRect(inflate
|
|
6547
|
+
})}` + roundedRect(inflate(window, -.5), r - .5, {
|
|
6415
6548
|
fill: "none",
|
|
6416
6549
|
stroke: style.border
|
|
6417
6550
|
});
|
|
@@ -6737,7 +6870,7 @@ const frame$4 = {
|
|
|
6737
6870
|
}]));
|
|
6738
6871
|
return out.join("");
|
|
6739
6872
|
},
|
|
6740
|
-
border: (_context, window) => roundedRect(inflate
|
|
6873
|
+
border: (_context, window) => roundedRect(inflate(window, -.5), window.radius - .5, {
|
|
6741
6874
|
fill: "none",
|
|
6742
6875
|
stroke: COLORS$3.border
|
|
6743
6876
|
})
|
|
@@ -6856,7 +6989,7 @@ const frame$3 = {
|
|
|
6856
6989
|
}));
|
|
6857
6990
|
return out.join("");
|
|
6858
6991
|
},
|
|
6859
|
-
border: ({ mode }, window) => roundedRect(inflate
|
|
6992
|
+
border: ({ mode }, window) => roundedRect(inflate(window, .5), window.radius + .5, {
|
|
6860
6993
|
fill: "none",
|
|
6861
6994
|
stroke: COLORS$2[mode].border
|
|
6862
6995
|
})
|
|
@@ -7025,7 +7158,7 @@ function blur(id, radius, box) {
|
|
|
7025
7158
|
return `<filter ${attrs({
|
|
7026
7159
|
id,
|
|
7027
7160
|
filterUnits: "userSpaceOnUse",
|
|
7028
|
-
...inflate
|
|
7161
|
+
...inflate(box, radius * 3)
|
|
7029
7162
|
})}><feGaussianBlur stdDeviation="${radius}"/></filter>`;
|
|
7030
7163
|
}
|
|
7031
7164
|
/** A thin line of light from sky blue to pink on the lower grid line, which glows downward */
|
|
@@ -7138,11 +7271,11 @@ const frame$2 = {
|
|
|
7138
7271
|
return line(header.x, y, header.x + header.width, y, { stroke: scene.frame.divider }) + controls(window, scene.frame.control) + titleBarText(context, window, { title: scene.frame.title });
|
|
7139
7272
|
},
|
|
7140
7273
|
border: ({ mode }, window) => {
|
|
7141
|
-
const out = [roundedRect(inflate
|
|
7274
|
+
const out = [roundedRect(inflate(window, -.5), window.radius - .5, {
|
|
7142
7275
|
fill: "none",
|
|
7143
7276
|
stroke: COLORS$1[mode].border
|
|
7144
7277
|
})];
|
|
7145
|
-
if (mode === "light") out.push(roundedRect(inflate
|
|
7278
|
+
if (mode === "light") out.push(roundedRect(inflate(window, .5), window.radius + .5, {
|
|
7146
7279
|
fill: "none",
|
|
7147
7280
|
stroke: "rgba(15, 23, 42, 0.1)"
|
|
7148
7281
|
}));
|
|
@@ -7960,11 +8093,25 @@ const SOFTWARE = "tin.so";
|
|
|
7960
8093
|
const PNG_KEYWORDS = {
|
|
7961
8094
|
software: "Software",
|
|
7962
8095
|
source: "Source",
|
|
7963
|
-
link: "
|
|
7964
|
-
spec: "code-image:spec"
|
|
8096
|
+
link: "tin.so:link"
|
|
7965
8097
|
};
|
|
7966
8098
|
/** The namespace of the metadata element in an SVG */
|
|
7967
|
-
const SVG_NAMESPACE = "urn:
|
|
8099
|
+
const SVG_NAMESPACE = "urn:tin.so:metadata:1";
|
|
8100
|
+
/**
|
|
8101
|
+
* The keywords and the SVG element of the first image format, which stored the spec a second time
|
|
8102
|
+
* next to the link. The reader still reads them, so that older images open.
|
|
8103
|
+
*/
|
|
8104
|
+
const LEGACY = {
|
|
8105
|
+
link: "code-image:link",
|
|
8106
|
+
spec: "code-image:spec",
|
|
8107
|
+
element: "code-image",
|
|
8108
|
+
namespace: "urn:code-image:metadata:1"
|
|
8109
|
+
};
|
|
8110
|
+
const OUR_KEYWORDS = /* @__PURE__ */ new Set([
|
|
8111
|
+
PNG_KEYWORDS.link,
|
|
8112
|
+
LEGACY.link,
|
|
8113
|
+
LEGACY.spec
|
|
8114
|
+
]);
|
|
7968
8115
|
const MAX_INPUT_BYTES = 67108864;
|
|
7969
8116
|
const MAX_CHUNK_BYTES = 4194304;
|
|
7970
8117
|
const MAX_SPEC_BYTES = 16777216;
|
|
@@ -7990,27 +8137,20 @@ async function createImageMetadata(spec, appUrl = DEFAULT_APP_URL) {
|
|
|
7990
8137
|
link: await createLink(stamped, appUrl)
|
|
7991
8138
|
};
|
|
7992
8139
|
}
|
|
7993
|
-
/**
|
|
7994
|
-
* The spec as JSON that is safe in XML text. JSON escapes the control characters, and this escapes
|
|
7995
|
-
* the two noncharacters that XML does not allow. Both are only possible in JSON strings.
|
|
7996
|
-
*/
|
|
7997
|
-
function specJson(spec) {
|
|
7998
|
-
return (JSON.stringify(spec) ?? "null").replace(/[]/g, (char) => `\\u${char.charCodeAt(0).toString(16)}`);
|
|
7999
|
-
}
|
|
8000
8140
|
/** The app URL of a link, without the spec in the fragment */
|
|
8001
8141
|
function sourceUrl(link) {
|
|
8002
8142
|
const url = new URL(link);
|
|
8003
8143
|
url.hash = "";
|
|
8004
8144
|
return url.toString();
|
|
8005
8145
|
}
|
|
8006
|
-
/** The metadata element of an SVG */
|
|
8146
|
+
/** The metadata element of an SVG: the program, the app URL and the editor link */
|
|
8007
8147
|
function svgMetadata(metadata) {
|
|
8008
|
-
return `<metadata><
|
|
8148
|
+
return `<metadata><tin ${[
|
|
8009
8149
|
`xmlns="${SVG_NAMESPACE}"`,
|
|
8010
8150
|
`software="${SOFTWARE}"`,
|
|
8011
8151
|
`source="${escapeXml(sourceUrl(metadata.link))}"`,
|
|
8012
8152
|
`link="${escapeXml(metadata.link)}"`
|
|
8013
|
-
].join(" ")}
|
|
8153
|
+
].join(" ")}/></metadata>`;
|
|
8014
8154
|
}
|
|
8015
8155
|
const CRC_TABLE = (() => {
|
|
8016
8156
|
const table = /* @__PURE__ */ new Uint32Array(256);
|
|
@@ -8053,60 +8193,20 @@ function textChunk(keyword, text) {
|
|
|
8053
8193
|
encoder.encode(text)
|
|
8054
8194
|
]));
|
|
8055
8195
|
}
|
|
8056
|
-
function compressedTextChunk(keyword, compressed) {
|
|
8057
|
-
return pngChunk("iTXt", concat([
|
|
8058
|
-
encoder.encode(keyword),
|
|
8059
|
-
new Uint8Array([
|
|
8060
|
-
0,
|
|
8061
|
-
1,
|
|
8062
|
-
0,
|
|
8063
|
-
0,
|
|
8064
|
-
0
|
|
8065
|
-
]),
|
|
8066
|
-
compressed
|
|
8067
|
-
]));
|
|
8068
|
-
}
|
|
8069
|
-
async function deflate(bytes) {
|
|
8070
|
-
const stream = new Blob([bytes]).stream().pipeThrough(new CompressionStream("deflate"));
|
|
8071
|
-
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
8072
|
-
}
|
|
8073
|
-
/** Inflates zlib data, or returns undefined when the data is bad or larger than the limit */
|
|
8074
|
-
async function inflate(bytes, limit) {
|
|
8075
|
-
const reader = new Blob([bytes]).stream().pipeThrough(new DecompressionStream("deflate")).getReader();
|
|
8076
|
-
const parts = [];
|
|
8077
|
-
let size = 0;
|
|
8078
|
-
try {
|
|
8079
|
-
for (;;) {
|
|
8080
|
-
const { done, value } = await reader.read();
|
|
8081
|
-
if (done) break;
|
|
8082
|
-
size += value.length;
|
|
8083
|
-
if (size > limit) {
|
|
8084
|
-
await reader.cancel().catch(() => void 0);
|
|
8085
|
-
return;
|
|
8086
|
-
}
|
|
8087
|
-
parts.push(value);
|
|
8088
|
-
}
|
|
8089
|
-
} catch {
|
|
8090
|
-
return;
|
|
8091
|
-
}
|
|
8092
|
-
return concat(parts);
|
|
8093
|
-
}
|
|
8094
8196
|
function isPng(bytes) {
|
|
8095
8197
|
return PNG_SIGNATURE.every((byte, index) => bytes[index] === byte);
|
|
8096
8198
|
}
|
|
8097
8199
|
/**
|
|
8098
|
-
* Adds text chunks after the header of a PNG: the program, the app URL
|
|
8099
|
-
*
|
|
8200
|
+
* Adds text chunks after the header of a PNG: the program, the app URL and the editor link. A PNG
|
|
8201
|
+
* without the expected header comes back unchanged.
|
|
8100
8202
|
*/
|
|
8101
8203
|
async function addPngMetadata(png, metadata) {
|
|
8102
8204
|
if (!isPng(png) || png.length < IHDR_END || decoder.decode(png.subarray(12, 16)) !== "IHDR") return png;
|
|
8103
|
-
const spec = await deflate(encoder.encode(specJson(metadata.spec)));
|
|
8104
8205
|
return concat([
|
|
8105
8206
|
png.subarray(0, IHDR_END),
|
|
8106
8207
|
textChunk(PNG_KEYWORDS.software, SOFTWARE),
|
|
8107
8208
|
textChunk(PNG_KEYWORDS.source, sourceUrl(metadata.link)),
|
|
8108
8209
|
textChunk(PNG_KEYWORDS.link, metadata.link),
|
|
8109
|
-
compressedTextChunk(PNG_KEYWORDS.spec, spec),
|
|
8110
8210
|
png.subarray(IHDR_END)
|
|
8111
8211
|
]);
|
|
8112
8212
|
}
|
|
@@ -8115,7 +8215,7 @@ function readTextChunk(type, data) {
|
|
|
8115
8215
|
const end = data.indexOf(0);
|
|
8116
8216
|
if (end < 1 || end > 79) return void 0;
|
|
8117
8217
|
const keyword = decoder.decode(data.subarray(0, end));
|
|
8118
|
-
if (
|
|
8218
|
+
if (!OUR_KEYWORDS.has(keyword)) return void 0;
|
|
8119
8219
|
if (type === "tEXt") return {
|
|
8120
8220
|
keyword,
|
|
8121
8221
|
entry: {
|
|
@@ -8160,7 +8260,7 @@ function readPngEntries(png) {
|
|
|
8160
8260
|
async function entryText(entry) {
|
|
8161
8261
|
if (!entry) return void 0;
|
|
8162
8262
|
if (!entry.compressed) return decoder.decode(entry.text);
|
|
8163
|
-
const bytes = await inflate(entry.text, MAX_SPEC_BYTES);
|
|
8263
|
+
const bytes = await inflate$1(entry.text, "deflate", MAX_SPEC_BYTES);
|
|
8164
8264
|
return bytes && decoder.decode(bytes);
|
|
8165
8265
|
}
|
|
8166
8266
|
function parseSpecJson(json) {
|
|
@@ -8175,7 +8275,7 @@ function parseSpecJson(json) {
|
|
|
8175
8275
|
async function specFromLink(link) {
|
|
8176
8276
|
if (!link) return void 0;
|
|
8177
8277
|
try {
|
|
8178
|
-
const spec = await readLink(link);
|
|
8278
|
+
const spec = await readLink(link, { maxBytes: MAX_SPEC_BYTES });
|
|
8179
8279
|
return typeof spec === "object" && spec !== null ? spec : void 0;
|
|
8180
8280
|
} catch {
|
|
8181
8281
|
return;
|
|
@@ -8183,7 +8283,9 @@ async function specFromLink(link) {
|
|
|
8183
8283
|
}
|
|
8184
8284
|
async function readPngSpec(png) {
|
|
8185
8285
|
const entries = readPngEntries(png);
|
|
8186
|
-
|
|
8286
|
+
const fromLink = await specFromLink(await entryText(entries.get(PNG_KEYWORDS.link)));
|
|
8287
|
+
if (fromLink !== void 0) return fromLink;
|
|
8288
|
+
return parseSpecJson(await entryText(entries.get(LEGACY.spec))) ?? specFromLink(await entryText(entries.get(LEGACY.link)));
|
|
8187
8289
|
}
|
|
8188
8290
|
const ENTITIES = {
|
|
8189
8291
|
amp: "&",
|
|
@@ -8199,18 +8301,32 @@ function unescapeXml(text) {
|
|
|
8199
8301
|
return code <= 1114111 ? String.fromCodePoint(code) : match;
|
|
8200
8302
|
});
|
|
8201
8303
|
}
|
|
8304
|
+
/** The value of the link attribute of an element tag, unescaped */
|
|
8305
|
+
function linkAttribute(tag) {
|
|
8306
|
+
const link = /\slink=(?:"([^"]*)"|'([^']*)')/.exec(tag);
|
|
8307
|
+
return link ? unescapeXml(link[1] ?? link[2]) : void 0;
|
|
8308
|
+
}
|
|
8202
8309
|
function readSvgSpec(svg) {
|
|
8203
|
-
const start = svg.indexOf("<
|
|
8310
|
+
const start = svg.indexOf("<tin ");
|
|
8311
|
+
if (start >= 0) {
|
|
8312
|
+
const end = svg.indexOf(">", start);
|
|
8313
|
+
const tag = end < 0 ? "" : svg.slice(start, end);
|
|
8314
|
+
if (tag.includes("urn:tin.so:metadata:1")) return specFromLink(linkAttribute(tag));
|
|
8315
|
+
}
|
|
8316
|
+
return readLegacySvgSpec(svg);
|
|
8317
|
+
}
|
|
8318
|
+
/** An SVG of the first format, with the spec as the text of the element and the link */
|
|
8319
|
+
function readLegacySvgSpec(svg) {
|
|
8320
|
+
const start = svg.indexOf(`<${LEGACY.element} `);
|
|
8204
8321
|
if (start < 0) return void 0;
|
|
8205
8322
|
const tagEnd = svg.indexOf(">", start);
|
|
8206
|
-
const close = svg.indexOf(
|
|
8323
|
+
const close = svg.indexOf(`</${LEGACY.element}>`, tagEnd);
|
|
8207
8324
|
if (tagEnd < 0 || close < 0) return void 0;
|
|
8208
8325
|
const tag = svg.slice(start, tagEnd);
|
|
8209
|
-
if (!tag.includes(
|
|
8326
|
+
if (!tag.includes(LEGACY.namespace)) return void 0;
|
|
8210
8327
|
const spec = parseSpecJson(unescapeXml(svg.slice(tagEnd + 1, close)));
|
|
8211
8328
|
if (spec !== void 0) return Promise.resolve(spec);
|
|
8212
|
-
|
|
8213
|
-
return specFromLink(link ? unescapeXml(link[1] ?? link[2]) : void 0);
|
|
8329
|
+
return specFromLink(linkAttribute(tag));
|
|
8214
8330
|
}
|
|
8215
8331
|
/**
|
|
8216
8332
|
* Reads the spec from a PNG or an SVG that this package made with metadata, with the fields of
|
|
@@ -8543,4 +8659,4 @@ function toSvg(scene, options = {}) {
|
|
|
8543
8659
|
"</svg>"
|
|
8544
8660
|
].join("\n");
|
|
8545
8661
|
}
|
|
8546
|
-
export {
|
|
8662
|
+
export { migrateLegacySpec as $, SpecSchema as A, LANGUAGE_IDS as B, isSpecSchemaUrl as C, MARK_STYLES as D, LATEST_SPEC_VERSION as E, THEME_IDS as F, resolveLanguage as G, highlight as H, getFrameColors as I, decodeSpec as J, DEFAULT_APP_URL as K, getSyntax as L, parseSpec as M, DEFAULT_THEME as N, SPEC_LIMITS as O, THEMES as P, IMPLICIT_SPEC_VERSION as Q, getTheme as R, SPEC_SCHEMA_URL as S, ANNOTATION_TYPES as T, inferLanguage as U, PLAIN_TEXT as V, isKnownLanguage as W, readLink as X, encodeSpec as Y, EXTENSION_PREFIX as Z, WINDOW_RADIUS as _, svgToPng as a, NO_ICON as at, normalizeCode as b, SOFTWARE as c, CODE_FONTS as ct, createImageMetadata as d, UI_FONT as dt, schemaVersion as et, readImageSpec as f, cssFontFamily as ft, TAB_HEADER as g, POPOVER as h, measureUiText as ht, sceneFontFiles as i, ICON_IDS as it, expandLineRanges as j, SpecError as k, SVG_NAMESPACE as l, CODE_FONT_IDS as lt, LINE_HEIGHT as m, getFace as mt, initPng as n, AUTO_ICON as nt, METADATA_BY_DEFAULT as o, getIcon as ot, FONT_SIZE as p, getCodeFont as pt, createLink as q, loadSceneFonts as r, FILE_ICON as rt, PNG_KEYWORDS as s, resolveIcon as st, toSvg as t, stampSpecVersion as tt, addPngMetadata as u, DEFAULT_CODE_FONT as ut, cellCount as v, specJsonSchema as w, MAC_HEADER as x, layout as y, resolveMode as z };
|
package/package.json
CHANGED
|
@@ -1,29 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tin.so/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Renders code images from a JSON spec to SVG and PNG",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "Chris Cook",
|
|
7
|
-
"homepage": "https://tin.so",
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/zirkelc/code-image.git"
|
|
11
|
-
},
|
|
12
5
|
"keywords": [
|
|
6
|
+
"cli",
|
|
13
7
|
"code",
|
|
14
|
-
"screenshot",
|
|
15
|
-
"image",
|
|
16
8
|
"code-image",
|
|
9
|
+
"image",
|
|
17
10
|
"ray.so",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
11
|
+
"screenshot",
|
|
12
|
+
"shiki"
|
|
20
13
|
],
|
|
21
|
-
"
|
|
22
|
-
|
|
14
|
+
"homepage": "https://tin.so",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "Chris Cook",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/zirkelc/tin.so.git"
|
|
23
20
|
},
|
|
24
21
|
"files": [
|
|
25
22
|
"dist",
|
|
26
|
-
"fonts"
|
|
23
|
+
"fonts",
|
|
24
|
+
"THIRD_PARTY_NOTICES.md"
|
|
27
25
|
],
|
|
28
26
|
"type": "module",
|
|
29
27
|
"exports": {
|
|
@@ -31,6 +29,9 @@
|
|
|
31
29
|
"./node": "./dist/node.mjs",
|
|
32
30
|
"./package.json": "./package.json"
|
|
33
31
|
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
34
35
|
"dependencies": {
|
|
35
36
|
"@resvg/resvg-wasm": "^2.6.2",
|
|
36
37
|
"@valibot/to-json-schema": "^1.8.0",
|