@tldraw/tlschema 4.6.0-next.d8328a2dcc3d → 4.6.0-next.e390fde97eab

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.
Files changed (79) hide show
  1. package/dist-cjs/assets/TLBaseAsset.js +4 -3
  2. package/dist-cjs/assets/TLBaseAsset.js.map +2 -2
  3. package/dist-cjs/assets/TLBookmarkAsset.js +9 -7
  4. package/dist-cjs/assets/TLBookmarkAsset.js.map +2 -2
  5. package/dist-cjs/assets/TLImageAsset.js +12 -10
  6. package/dist-cjs/assets/TLImageAsset.js.map +2 -2
  7. package/dist-cjs/assets/TLVideoAsset.js +11 -9
  8. package/dist-cjs/assets/TLVideoAsset.js.map +2 -2
  9. package/dist-cjs/createTLSchema.js +10 -4
  10. package/dist-cjs/createTLSchema.js.map +2 -2
  11. package/dist-cjs/index.d.ts +601 -343
  12. package/dist-cjs/index.js +17 -7
  13. package/dist-cjs/index.js.map +2 -2
  14. package/dist-cjs/records/TLAsset.js +29 -14
  15. package/dist-cjs/records/TLAsset.js.map +2 -2
  16. package/dist-cjs/shapes/TLFrameShape.js +3 -1
  17. package/dist-cjs/shapes/TLFrameShape.js.map +2 -2
  18. package/dist-cjs/shapes/TLNoteShape.js +12 -2
  19. package/dist-cjs/shapes/TLNoteShape.js.map +2 -2
  20. package/dist-cjs/styles/StyleProp.js +38 -1
  21. package/dist-cjs/styles/StyleProp.js.map +2 -2
  22. package/dist-cjs/styles/TLColorStyle.js +36 -487
  23. package/dist-cjs/styles/TLColorStyle.js.map +2 -2
  24. package/dist-cjs/styles/TLDashStyle.js +1 -1
  25. package/dist-cjs/styles/TLDashStyle.js.map +2 -2
  26. package/dist-cjs/styles/TLFontFace.js +17 -0
  27. package/dist-cjs/styles/TLFontFace.js.map +7 -0
  28. package/dist-cjs/styles/TLFontStyle.js +36 -1
  29. package/dist-cjs/styles/TLFontStyle.js.map +2 -2
  30. package/dist-cjs/styles/TLTheme.js +17 -0
  31. package/dist-cjs/styles/TLTheme.js.map +7 -0
  32. package/dist-esm/assets/TLBaseAsset.mjs +4 -3
  33. package/dist-esm/assets/TLBaseAsset.mjs.map +2 -2
  34. package/dist-esm/assets/TLBookmarkAsset.mjs +9 -7
  35. package/dist-esm/assets/TLBookmarkAsset.mjs.map +2 -2
  36. package/dist-esm/assets/TLImageAsset.mjs +12 -10
  37. package/dist-esm/assets/TLImageAsset.mjs.map +2 -2
  38. package/dist-esm/assets/TLVideoAsset.mjs +11 -9
  39. package/dist-esm/assets/TLVideoAsset.mjs.map +2 -2
  40. package/dist-esm/createTLSchema.mjs +17 -8
  41. package/dist-esm/createTLSchema.mjs.map +2 -2
  42. package/dist-esm/index.d.mts +601 -343
  43. package/dist-esm/index.mjs +28 -14
  44. package/dist-esm/index.mjs.map +2 -2
  45. package/dist-esm/records/TLAsset.mjs +29 -14
  46. package/dist-esm/records/TLAsset.mjs.map +2 -2
  47. package/dist-esm/shapes/TLFrameShape.mjs +3 -1
  48. package/dist-esm/shapes/TLFrameShape.mjs.map +2 -2
  49. package/dist-esm/shapes/TLNoteShape.mjs +12 -2
  50. package/dist-esm/shapes/TLNoteShape.mjs.map +2 -2
  51. package/dist-esm/styles/StyleProp.mjs +38 -1
  52. package/dist-esm/styles/StyleProp.mjs.map +2 -2
  53. package/dist-esm/styles/TLColorStyle.mjs +36 -487
  54. package/dist-esm/styles/TLColorStyle.mjs.map +2 -2
  55. package/dist-esm/styles/TLDashStyle.mjs +1 -1
  56. package/dist-esm/styles/TLDashStyle.mjs.map +2 -2
  57. package/dist-esm/styles/TLFontFace.mjs +1 -0
  58. package/dist-esm/styles/TLFontFace.mjs.map +7 -0
  59. package/dist-esm/styles/TLFontStyle.mjs +36 -1
  60. package/dist-esm/styles/TLFontStyle.mjs.map +2 -2
  61. package/dist-esm/styles/TLTheme.mjs +1 -0
  62. package/dist-esm/styles/TLTheme.mjs.map +7 -0
  63. package/package.json +5 -5
  64. package/src/assets/TLBaseAsset.ts +25 -28
  65. package/src/assets/TLBookmarkAsset.ts +13 -33
  66. package/src/assets/TLImageAsset.ts +16 -42
  67. package/src/assets/TLVideoAsset.ts +15 -40
  68. package/src/createTLSchema.ts +54 -15
  69. package/src/index.ts +31 -11
  70. package/src/migrations.test.ts +22 -0
  71. package/src/records/TLAsset.ts +139 -74
  72. package/src/shapes/TLFrameShape.ts +3 -1
  73. package/src/shapes/TLNoteShape.ts +19 -5
  74. package/src/styles/StyleProp.ts +42 -5
  75. package/src/styles/TLColorStyle.ts +55 -698
  76. package/src/styles/TLDashStyle.ts +1 -1
  77. package/src/styles/TLFontFace.ts +65 -0
  78. package/src/styles/TLFontStyle.ts +53 -4
  79. package/src/styles/TLTheme.ts +240 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -9,8 +9,43 @@ const DefaultFontFamilies = {
9
9
  serif: "'tldraw_serif', serif",
10
10
  mono: "'tldraw_mono', monospace"
11
11
  };
12
+ function isFontEntry(value) {
13
+ return typeof value === "object" && value !== null && "fontFamily" in value;
14
+ }
15
+ function registerFontsFromThemes(definitions) {
16
+ const fontNames = /* @__PURE__ */ new Set();
17
+ for (const def of Object.values(definitions)) {
18
+ if (!def.fonts) continue;
19
+ for (const [key, value] of Object.entries(def.fonts)) {
20
+ if (isFontEntry(value)) {
21
+ fontNames.add(key);
22
+ }
23
+ }
24
+ }
25
+ const toAdd = [...fontNames].filter((v) => !DefaultFontStyle.values.includes(v));
26
+ if (toAdd.length > 0) {
27
+ DefaultFontStyle.addValues(...toAdd);
28
+ }
29
+ const toRemove = DefaultFontStyle.values.filter((v) => !fontNames.has(v));
30
+ if (toRemove.length > 0) {
31
+ DefaultFontStyle.removeValues(...toRemove);
32
+ }
33
+ if (process.env.NODE_ENV !== "production") {
34
+ for (const def of Object.values(definitions)) {
35
+ for (const font of fontNames) {
36
+ if (!def.fonts || !(font in def.fonts)) {
37
+ console.warn(
38
+ `Theme '${def.id}' is missing font '${font}'. Shapes using this font won't render correctly in this theme.`
39
+ );
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
12
45
  export {
13
46
  DefaultFontFamilies,
14
- DefaultFontStyle
47
+ DefaultFontStyle,
48
+ isFontEntry,
49
+ registerFontsFromThemes
15
50
  };
16
51
  //# sourceMappingURL=TLFontStyle.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/styles/TLFontStyle.ts"],
4
- "sourcesContent": ["import { T } from '@tldraw/validate'\nimport { StyleProp } from './StyleProp'\n\n/**\n * Default font style property used by tldraw shapes for text styling.\n * Controls which typeface is used for text content within shapes.\n *\n * Available values:\n * - `draw` - Hand-drawn, sketchy font style\n * - `sans` - Clean sans-serif font\n * - `serif` - Traditional serif font\n * - `mono` - Monospace font for code-like text\n *\n * @example\n * ```ts\n * import { DefaultFontStyle } from '@tldraw/tlschema'\n *\n * // Use in shape props definition\n * interface MyTextShapeProps {\n * font: typeof DefaultFontStyle\n * // other props...\n * }\n *\n * // Create a text shape with monospace font\n * const textShape = {\n * // ... other properties\n * props: {\n * font: 'mono' as const,\n * // ... other props\n * }\n * }\n * ```\n *\n * @public\n */\nexport const DefaultFontStyle = StyleProp.defineEnum('tldraw:font', {\n\tdefaultValue: 'draw',\n\tvalues: ['draw', 'sans', 'serif', 'mono'],\n})\n\n/**\n * Type representing a default font style value.\n * This is a union type of all available font style options.\n *\n * @example\n * ```ts\n * import { TLDefaultFontStyle } from '@tldraw/tlschema'\n *\n * // Valid font style values\n * const drawFont: TLDefaultFontStyle = 'draw'\n * const sansFont: TLDefaultFontStyle = 'sans'\n * const serifFont: TLDefaultFontStyle = 'serif'\n * const monoFont: TLDefaultFontStyle = 'mono'\n *\n * // Use in a function parameter\n * function setTextFont(font: TLDefaultFontStyle) {\n * // Apply font style to text\n * }\n * ```\n *\n * @public\n */\nexport type TLDefaultFontStyle = T.TypeOf<typeof DefaultFontStyle>\n\n/**\n * Mapping of font style names to their corresponding CSS font-family declarations.\n * These are the actual CSS font families used when rendering text with each font style.\n *\n * @example\n * ```ts\n * import { DefaultFontFamilies, TLDefaultFontStyle } from '@tldraw/tlschema'\n *\n * // Get CSS font family for a font style\n * const fontStyle: TLDefaultFontStyle = 'mono'\n * const cssFamily = DefaultFontFamilies[fontStyle] // \"'tldraw_mono', monospace\"\n *\n * // Apply to DOM element\n * element.style.fontFamily = DefaultFontFamilies.sans\n * ```\n *\n * @public\n */\nexport const DefaultFontFamilies = {\n\tdraw: \"'tldraw_draw', sans-serif\",\n\tsans: \"'tldraw_sans', sans-serif\",\n\tserif: \"'tldraw_serif', serif\",\n\tmono: \"'tldraw_mono', monospace\",\n}\n"],
5
- "mappings": "AACA,SAAS,iBAAiB;AAkCnB,MAAM,mBAAmB,UAAU,WAAW,eAAe;AAAA,EACnE,cAAc;AAAA,EACd,QAAQ,CAAC,QAAQ,QAAQ,SAAS,MAAM;AACzC,CAAC;AA4CM,MAAM,sBAAsB;AAAA,EAClC,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AACP;",
4
+ "sourcesContent": ["import { StyleProp } from './StyleProp'\nimport { TLThemeFont, TLThemeFonts, TLThemes } from './TLTheme'\n\n/**\n * Default font style property used by tldraw shapes for text styling.\n * Controls which typeface is used for text content within shapes.\n *\n * Available values:\n * - `draw` - Hand-drawn, sketchy font style\n * - `sans` - Clean sans-serif font\n * - `serif` - Traditional serif font\n * - `mono` - Monospace font for code-like text\n *\n * @example\n * ```ts\n * import { DefaultFontStyle } from '@tldraw/tlschema'\n *\n * // Use in shape props definition\n * interface MyTextShapeProps {\n * font: typeof DefaultFontStyle\n * // other props...\n * }\n *\n * // Create a text shape with monospace font\n * const textShape = {\n * // ... other properties\n * props: {\n * font: 'mono' as const,\n * // ... other props\n * }\n * }\n * ```\n *\n * @public\n */\nexport const DefaultFontStyle = StyleProp.defineEnum('tldraw:font', {\n\tdefaultValue: 'draw',\n\tvalues: ['draw', 'sans', 'serif', 'mono'],\n})\n\n/**\n * The names of all available font styles, derived from {@link TLThemeFonts}.\n * Extend {@link TLThemeFonts} to add custom font names.\n *\n * @example\n * ```ts\n * import { TLDefaultFontStyle } from '@tldraw/tlschema'\n *\n * // Valid font style values\n * const drawFont: TLDefaultFontStyle = 'draw'\n * const sansFont: TLDefaultFontStyle = 'sans'\n * const serifFont: TLDefaultFontStyle = 'serif'\n * const monoFont: TLDefaultFontStyle = 'mono'\n *\n * // Use in a function parameter\n * function setTextFont(font: TLDefaultFontStyle) {\n * // Apply font style to text\n * }\n * ```\n *\n * @public\n */\nexport type TLDefaultFontStyle = keyof TLThemeFonts & string\n\n/**\n * Mapping of font style names to their corresponding CSS font-family declarations.\n * These are the actual CSS font families used when rendering text with each font style.\n *\n * @example\n * ```ts\n * import { DefaultFontFamilies, TLDefaultFontStyle } from '@tldraw/tlschema'\n *\n * // Get CSS font family for a font style\n * const fontStyle: TLDefaultFontStyle = 'mono'\n * const cssFamily = DefaultFontFamilies[fontStyle] // \"'tldraw_mono', monospace\"\n *\n * // Apply to DOM element\n * element.style.fontFamily = DefaultFontFamilies.sans\n * ```\n *\n * @public\n */\nexport const DefaultFontFamilies = {\n\tdraw: \"'tldraw_draw', sans-serif\",\n\tsans: \"'tldraw_sans', sans-serif\",\n\tserif: \"'tldraw_serif', serif\",\n\tmono: \"'tldraw_mono', monospace\",\n}\n\n/** @internal */\nexport function isFontEntry(value: unknown): value is TLThemeFont {\n\treturn typeof value === 'object' && value !== null && 'fontFamily' in value\n}\n\n/**\n * Scan theme definitions and sync font registrations to match.\n * A font entry is any key in `TLThemeFonts` whose value is a {@link TLThemeFont}\n * object (i.e. has a `fontFamily` property).\n *\n * Fonts present in themes but not yet registered will be added.\n * Fonts currently registered but absent from all themes will be removed.\n *\n * @public\n */\nexport function registerFontsFromThemes(definitions: TLThemes): void {\n\tconst fontNames = new Set<string>()\n\tfor (const def of Object.values(definitions)) {\n\t\tif (!def.fonts) continue\n\t\tfor (const [key, value] of Object.entries(def.fonts)) {\n\t\t\tif (isFontEntry(value)) {\n\t\t\t\tfontNames.add(key)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst toAdd = [...fontNames].filter((v) => !DefaultFontStyle.values.includes(v as any))\n\tif (toAdd.length > 0) {\n\t\tDefaultFontStyle.addValues(...(toAdd as any))\n\t}\n\n\tconst toRemove = DefaultFontStyle.values.filter((v) => !fontNames.has(v as string))\n\tif (toRemove.length > 0) {\n\t\tDefaultFontStyle.removeValues(...toRemove)\n\t}\n\n\tif (process.env.NODE_ENV !== 'production') {\n\t\tfor (const def of Object.values(definitions)) {\n\t\t\tfor (const font of fontNames) {\n\t\t\t\tif (!def.fonts || !(font in def.fonts)) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`Theme '${def.id}' is missing font '${font}'. Shapes using this font won't render correctly in this theme.`\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAmCnB,MAAM,mBAAmB,UAAU,WAAW,eAAe;AAAA,EACnE,cAAc;AAAA,EACd,QAAQ,CAAC,QAAQ,QAAQ,SAAS,MAAM;AACzC,CAAC;AA4CM,MAAM,sBAAsB;AAAA,EAClC,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AACP;AAGO,SAAS,YAAY,OAAsC;AACjE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,gBAAgB;AACvE;AAYO,SAAS,wBAAwB,aAA6B;AACpE,QAAM,YAAY,oBAAI,IAAY;AAClC,aAAW,OAAO,OAAO,OAAO,WAAW,GAAG;AAC7C,QAAI,CAAC,IAAI,MAAO;AAChB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,KAAK,GAAG;AACrD,UAAI,YAAY,KAAK,GAAG;AACvB,kBAAU,IAAI,GAAG;AAAA,MAClB;AAAA,IACD;AAAA,EACD;AAEA,QAAM,QAAQ,CAAC,GAAG,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,iBAAiB,OAAO,SAAS,CAAQ,CAAC;AACtF,MAAI,MAAM,SAAS,GAAG;AACrB,qBAAiB,UAAU,GAAI,KAAa;AAAA,EAC7C;AAEA,QAAM,WAAW,iBAAiB,OAAO,OAAO,CAAC,MAAM,CAAC,UAAU,IAAI,CAAW,CAAC;AAClF,MAAI,SAAS,SAAS,GAAG;AACxB,qBAAiB,aAAa,GAAG,QAAQ;AAAA,EAC1C;AAEA,MAAI,QAAQ,IAAI,aAAa,cAAc;AAC1C,eAAW,OAAO,OAAO,OAAO,WAAW,GAAG;AAC7C,iBAAW,QAAQ,WAAW;AAC7B,YAAI,CAAC,IAAI,SAAS,EAAE,QAAQ,IAAI,QAAQ;AACvC,kBAAQ;AAAA,YACP,UAAU,IAAI,EAAE,sBAAsB,IAAI;AAAA,UAC3C;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,EACD;AACD;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=TLTheme.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tldraw/tlschema",
3
3
  "description": "tldraw infinite canvas SDK (schema).",
4
- "version": "4.6.0-next.d8328a2dcc3d",
4
+ "version": "4.6.0-next.e390fde97eab",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -50,10 +50,10 @@
50
50
  "vitest": "^3.2.4"
51
51
  },
52
52
  "dependencies": {
53
- "@tldraw/state": "4.6.0-next.d8328a2dcc3d",
54
- "@tldraw/store": "4.6.0-next.d8328a2dcc3d",
55
- "@tldraw/utils": "4.6.0-next.d8328a2dcc3d",
56
- "@tldraw/validate": "4.6.0-next.d8328a2dcc3d"
53
+ "@tldraw/state": "4.6.0-next.e390fde97eab",
54
+ "@tldraw/store": "4.6.0-next.e390fde97eab",
55
+ "@tldraw/utils": "4.6.0-next.e390fde97eab",
56
+ "@tldraw/validate": "4.6.0-next.e390fde97eab"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "react": "^18.2.0 || ^19.2.1",
@@ -68,7 +68,8 @@ export const assetIdValidator = idValidator<TLAssetId>('asset')
68
68
  * base properties (id, typeName, type, meta) and the type-specific props.
69
69
  *
70
70
  * @param type - The asset type identifier (e.g., 'image', 'video', 'bookmark')
71
- * @param props - The validator for the asset's type-specific properties
71
+ * @param props - A validator or per-key validator record for the asset's type-specific properties
72
+ * @param meta - An optional per-key validator record for the asset's meta properties
72
73
  * @returns A complete validator for the asset record type
73
74
  *
74
75
  * @example
@@ -83,45 +84,41 @@ export const assetIdValidator = idValidator<TLAssetId>('asset')
83
84
  * description?: string
84
85
  * }>
85
86
  *
86
- * // Create validator for the custom asset
87
- * const customAssetValidator = createAssetValidator('custom', T.object({
87
+ * // Create validator using a per-key record (recommended)
88
+ * const customAssetValidator = createAssetValidator('custom', {
88
89
  * url: T.string,
89
90
  * title: T.string,
90
91
  * description: T.string.optional()
91
- * }))
92
- *
93
- * // Use the validator
94
- * const assetData = {
95
- * id: 'asset:custom123',
96
- * typeName: 'asset' as const,
97
- * type: 'custom' as const,
98
- * props: {
99
- * url: 'https://example.com',
100
- * title: 'My Custom Asset'
101
- * },
102
- * meta: {}
103
- * }
92
+ * })
104
93
  *
105
- * const validatedAsset = customAssetValidator.validate(assetData)
94
+ * // Or using a T.object validator
95
+ * const customAssetValidator2 = createAssetValidator('custom', T.object({
96
+ * url: T.string,
97
+ * title: T.string,
98
+ * description: T.string.optional()
99
+ * }))
106
100
  * ```
107
101
  *
108
102
  * @public
109
103
  */
110
- export function createAssetValidator<Type extends string, Props extends JsonObject>(
104
+ export function createAssetValidator<
105
+ Type extends string,
106
+ Props extends JsonObject,
107
+ Meta extends JsonObject = JsonObject,
108
+ >(
111
109
  type: Type,
112
- props: T.Validator<Props>
110
+ props?: T.Validator<Props> | { [K in keyof Props]: T.Validatable<Props[K]> },
111
+ meta?: { [K in keyof Meta]: T.Validatable<Meta[K]> }
113
112
  ) {
114
- return T.object<{
115
- id: TLAssetId
116
- typeName: 'asset'
117
- type: Type
118
- props: Props
119
- meta: JsonObject
120
- }>({
113
+ // Determine if props is a Validator instance or a per-key record
114
+ const propsValidator =
115
+ props instanceof T.Validator ? props : props ? T.object(props) : (T.jsonValue as any)
116
+
117
+ return T.object<TLBaseAsset<Type, Props>>({
121
118
  id: assetIdValidator,
122
119
  typeName: T.literal('asset'),
123
120
  type: T.literal(type),
124
- props,
125
- meta: T.jsonValue as T.ObjectValidator<JsonObject>,
121
+ props: propsValidator,
122
+ meta: meta ? T.object(meta) : (T.jsonValue as T.ObjectValidator<JsonObject>),
126
123
  })
127
124
  }
@@ -1,6 +1,6 @@
1
1
  import { createMigrationIds, createRecordMigrationSequence } from '@tldraw/store'
2
2
  import { T } from '@tldraw/validate'
3
- import { TLAsset } from '../records/TLAsset'
3
+ import { RecordProps } from '../recordsWithProps'
4
4
  import { TLBaseAsset, createAssetValidator } from './TLBaseAsset'
5
5
 
6
6
  /**
@@ -18,39 +18,19 @@ export type TLBookmarkAsset = TLBaseAsset<
18
18
  }
19
19
  >
20
20
 
21
- /**
22
- * Validator for TLBookmarkAsset records. Validates the structure and data types
23
- * of bookmark asset properties including title, description, image, favicon, and source URL.
24
- *
25
- * @example
26
- * ```ts
27
- * const bookmarkData = {
28
- * id: 'asset:bookmark1',
29
- * typeName: 'asset',
30
- * type: 'bookmark',
31
- * props: {
32
- * title: 'Example Website',
33
- * description: 'A great example site',
34
- * image: 'https://example.com/preview.jpg',
35
- * favicon: 'https://example.com/favicon.ico',
36
- * src: 'https://example.com'
37
- * }
38
- * }
39
- *
40
- * const isValid = bookmarkAssetValidator.isValid(bookmarkData)
41
- * ```
42
- *
43
- * @public
44
- */
21
+ /** @public */
22
+ export const bookmarkAssetProps = {
23
+ title: T.string,
24
+ description: T.string,
25
+ image: T.string,
26
+ favicon: T.string,
27
+ src: T.srcUrl.nullable(),
28
+ } satisfies RecordProps<TLBookmarkAsset>
29
+
30
+ /** Validator for bookmark assets. @public */
45
31
  export const bookmarkAssetValidator: T.Validator<TLBookmarkAsset> = createAssetValidator(
46
32
  'bookmark',
47
- T.object({
48
- title: T.string,
49
- description: T.string,
50
- image: T.string,
51
- favicon: T.string,
52
- src: T.srcUrl.nullable(),
53
- })
33
+ T.object(bookmarkAssetProps)
54
34
  )
55
35
 
56
36
  const Versions = createMigrationIds('com.tldraw.asset.bookmark', {
@@ -91,7 +71,7 @@ export { Versions as bookmarkAssetVersions }
91
71
  export const bookmarkAssetMigrations = createRecordMigrationSequence({
92
72
  sequenceId: 'com.tldraw.asset.bookmark',
93
73
  recordType: 'asset',
94
- filter: (asset) => (asset as TLAsset).type === 'bookmark',
74
+ filter: (asset) => (asset as TLBookmarkAsset).type === 'bookmark',
95
75
  sequence: [
96
76
  {
97
77
  id: Versions.MakeUrlsValid,
@@ -1,6 +1,6 @@
1
1
  import { createMigrationIds, createRecordMigrationSequence } from '@tldraw/store'
2
2
  import { T } from '@tldraw/validate'
3
- import { TLAsset } from '../records/TLAsset'
3
+ import { RecordProps } from '../recordsWithProps'
4
4
  import { TLBaseAsset, createAssetValidator } from './TLBaseAsset'
5
5
 
6
6
  /**
@@ -21,48 +21,22 @@ export type TLImageAsset = TLBaseAsset<
21
21
  }
22
22
  >
23
23
 
24
- /**
25
- * Validator for image assets. Validates the structure and properties of TLImageAsset records
26
- * to ensure data integrity when image assets are stored or retrieved from the tldraw store.
27
- *
28
- * @example
29
- * ```ts
30
- * import { imageAssetValidator } from '@tldraw/tlschema'
31
- *
32
- * const imageAsset = {
33
- * id: 'asset:image123',
34
- * typeName: 'asset',
35
- * type: 'image',
36
- * props: {
37
- * w: 800,
38
- * h: 600,
39
- * name: 'photo.jpg',
40
- * isAnimated: false,
41
- * mimeType: 'image/jpeg',
42
- * src: 'https://example.com/photo.jpg',
43
- * fileSize: 156000
44
- * },
45
- * meta: {}
46
- * }
47
- *
48
- * // Validate the asset
49
- * const isValid = imageAssetValidator.validate(imageAsset)
50
- * ```
51
- *
52
- * @public
53
- */
24
+ /** @public */
25
+ export const imageAssetProps = {
26
+ w: T.number,
27
+ h: T.number,
28
+ name: T.string,
29
+ isAnimated: T.boolean,
30
+ mimeType: T.string.nullable(),
31
+ src: T.srcUrl.nullable(),
32
+ fileSize: T.nonZeroNumber.optional(),
33
+ pixelRatio: T.positiveNumber.optional(),
34
+ } satisfies RecordProps<TLImageAsset>
35
+
36
+ /** Validator for image assets. @public */
54
37
  export const imageAssetValidator: T.Validator<TLImageAsset> = createAssetValidator(
55
38
  'image',
56
- T.object({
57
- w: T.number,
58
- h: T.number,
59
- name: T.string,
60
- isAnimated: T.boolean,
61
- mimeType: T.string.nullable(),
62
- src: T.srcUrl.nullable(),
63
- fileSize: T.nonZeroNumber.optional(),
64
- pixelRatio: T.positiveNumber.optional(),
65
- })
39
+ T.object(imageAssetProps)
66
40
  )
67
41
 
68
42
  const Versions = createMigrationIds('com.tldraw.asset.image', {
@@ -109,7 +83,7 @@ export { Versions as imageAssetVersions }
109
83
  export const imageAssetMigrations = createRecordMigrationSequence({
110
84
  sequenceId: 'com.tldraw.asset.image',
111
85
  recordType: 'asset',
112
- filter: (asset) => (asset as TLAsset).type === 'image',
86
+ filter: (asset) => (asset as TLImageAsset).type === 'image',
113
87
  sequence: [
114
88
  {
115
89
  id: Versions.AddIsAnimated,
@@ -1,6 +1,6 @@
1
1
  import { createMigrationIds, createRecordMigrationSequence } from '@tldraw/store'
2
2
  import { T } from '@tldraw/validate'
3
- import { TLAsset } from '../records/TLAsset'
3
+ import { RecordProps } from '../recordsWithProps'
4
4
  import { TLBaseAsset, createAssetValidator } from './TLBaseAsset'
5
5
 
6
6
  /**
@@ -52,46 +52,21 @@ export type TLVideoAsset = TLBaseAsset<
52
52
  }
53
53
  >
54
54
 
55
- /**
56
- * Runtime validator for TLVideoAsset records. This validator ensures that video asset
57
- * data conforms to the expected structure and types, providing type safety at runtime.
58
- * It validates dimensions, file metadata, and ensures URLs are properly formatted.
59
- *
60
- * @example
61
- * ```ts
62
- * import { videoAssetValidator } from '@tldraw/tlschema'
63
- *
64
- * // Validate a video asset object
65
- * const validAsset = videoAssetValidator.validate({
66
- * id: 'asset:video123',
67
- * typeName: 'asset',
68
- * type: 'video',
69
- * props: {
70
- * w: 1920,
71
- * h: 1080,
72
- * name: 'video.mp4',
73
- * isAnimated: true,
74
- * mimeType: 'video/mp4',
75
- * src: 'https://example.com/video.mp4',
76
- * fileSize: 1024000
77
- * },
78
- * meta: {}
79
- * })
80
- * ```
81
- *
82
- * @public
83
- */
55
+ /** @public */
56
+ export const videoAssetProps = {
57
+ w: T.number,
58
+ h: T.number,
59
+ name: T.string,
60
+ isAnimated: T.boolean,
61
+ mimeType: T.string.nullable(),
62
+ src: T.srcUrl.nullable(),
63
+ fileSize: T.number.optional(),
64
+ } satisfies RecordProps<TLVideoAsset>
65
+
66
+ /** Validator for video assets. @public */
84
67
  export const videoAssetValidator: T.Validator<TLVideoAsset> = createAssetValidator(
85
68
  'video',
86
- T.object({
87
- w: T.number,
88
- h: T.number,
89
- name: T.string,
90
- isAnimated: T.boolean,
91
- mimeType: T.string.nullable(),
92
- src: T.srcUrl.nullable(),
93
- fileSize: T.number.optional(),
94
- })
69
+ T.object(videoAssetProps)
95
70
  )
96
71
 
97
72
  const Versions = createMigrationIds('com.tldraw.asset.video', {
@@ -137,7 +112,7 @@ export { Versions as videoAssetVersions }
137
112
  export const videoAssetMigrations = createRecordMigrationSequence({
138
113
  sequenceId: 'com.tldraw.asset.video',
139
114
  recordType: 'asset',
140
- filter: (asset) => (asset as TLAsset).type === 'video',
115
+ filter: (asset) => (asset as TLVideoAsset).type === 'video',
141
116
  sequence: [
142
117
  {
143
118
  id: Versions.AddIsAnimated,
@@ -1,11 +1,17 @@
1
1
  import { LegacyMigrations, MigrationSequence, StoreSchema, StoreValidator } from '@tldraw/store'
2
2
  import { objectMapValues } from '@tldraw/utils'
3
3
  import { T } from '@tldraw/validate'
4
- import { bookmarkAssetMigrations } from './assets/TLBookmarkAsset'
5
- import { imageAssetMigrations } from './assets/TLImageAsset'
6
- import { videoAssetMigrations } from './assets/TLVideoAsset'
4
+ import { TLBaseAsset } from './assets/TLBaseAsset'
5
+ import { bookmarkAssetMigrations, bookmarkAssetProps } from './assets/TLBookmarkAsset'
6
+ import { imageAssetMigrations, imageAssetProps } from './assets/TLImageAsset'
7
+ import { videoAssetMigrations, videoAssetProps } from './assets/TLVideoAsset'
7
8
  import { arrowBindingMigrations, arrowBindingProps } from './bindings/TLArrowBinding'
8
- import { AssetRecordType, assetMigrations } from './records/TLAsset'
9
+ import {
10
+ TLDefaultAsset,
11
+ TLUnknownAsset,
12
+ assetMigrations,
13
+ createAssetRecordType,
14
+ } from './records/TLAsset'
9
15
  import { TLBinding, TLDefaultBinding, createBindingRecordType } from './records/TLBinding'
10
16
  import { CameraRecordType, cameraMigrations } from './records/TLCamera'
11
17
  import {
@@ -48,7 +54,7 @@ import { StyleProp } from './styles/StyleProp'
48
54
  import { TLStoreProps, createIntegrityChecker, onValidationFailure } from './TLStore'
49
55
 
50
56
  /**
51
- * Configuration information for a schema type (shape or binding), including its properties,
57
+ * Configuration information for a schema type (shape, binding, or asset), including its properties,
52
58
  * metadata, and migration sequences for data evolution over time.
53
59
  *
54
60
  * @public
@@ -73,12 +79,12 @@ export interface SchemaPropsInfo {
73
79
  migrations?: LegacyMigrations | TLPropsMigrations | MigrationSequence
74
80
 
75
81
  /**
76
- * Validation schema for the shape or binding properties. Maps property names to their validators.
82
+ * Validation schema for the shape, binding, or asset properties.
77
83
  */
78
84
  props?: Record<string, StoreValidator<any>>
79
85
 
80
86
  /**
81
- * Validation schema for metadata fields. Maps metadata field names to their validators.
87
+ * Validation schema for metadata fields.
82
88
  */
83
89
  meta?: Record<string, StoreValidator<any>>
84
90
  }
@@ -195,6 +201,30 @@ export const defaultBindingSchemas = {
195
201
  arrow: { migrations: arrowBindingMigrations, props: arrowBindingProps },
196
202
  } satisfies { [T in TLDefaultBinding['type']]: SchemaPropsInfo }
197
203
 
204
+ /**
205
+ * Default asset schema configurations for all built-in tldraw asset types.
206
+ *
207
+ * @public
208
+ * @example
209
+ * ```ts
210
+ * import { createTLSchema, defaultAssetSchemas } from '@tldraw/tlschema'
211
+ *
212
+ * const schema = createTLSchema({
213
+ * assets: defaultAssetSchemas,
214
+ * })
215
+ * ```
216
+ */
217
+ export const defaultAssetSchemas = {
218
+ image: { migrations: imageAssetMigrations, props: imageAssetProps },
219
+ video: { migrations: videoAssetMigrations, props: videoAssetProps },
220
+ bookmark: { migrations: bookmarkAssetMigrations, props: bookmarkAssetProps },
221
+ } satisfies {
222
+ [T in TLDefaultAsset['type']]: {
223
+ migrations: SchemaPropsInfo['migrations']
224
+ props: RecordProps<TLBaseAsset<T, Extract<TLDefaultAsset, { type: T }>['props']>>
225
+ }
226
+ }
227
+
198
228
  /**
199
229
  * Configuration for extending the user record type with custom metadata
200
230
  * validators and migration sequences.
@@ -232,12 +262,13 @@ export interface UserSchemaInfo {
232
262
  * validation, and migration sequences for all record types in a tldraw application.
233
263
  *
234
264
  * The schema includes all core record types (pages, cameras, instances, etc.) plus the
235
- * shape, binding, and custom record types you specify. Style properties are automatically
236
- * collected from all shapes to ensure consistency across the application.
265
+ * shape, binding, asset, and custom record types you specify. Style properties are
266
+ * automatically collected from all shapes to ensure consistency across the application.
237
267
  *
238
268
  * @param options - Configuration options for the schema
239
269
  * - shapes - Shape schema configurations. Defaults to defaultShapeSchemas if not provided
240
270
  * - bindings - Binding schema configurations. Defaults to defaultBindingSchemas if not provided
271
+ * - assets - Asset schema configurations. Defaults to defaultAssetSchemas if not provided
241
272
  * - user - Custom user record configuration with meta validators and migrations
242
273
  * - records - Custom record type configurations. These are additional record types beyond
243
274
  * the built-in shapes, bindings, assets, etc.
@@ -247,10 +278,15 @@ export interface UserSchemaInfo {
247
278
  * @public
248
279
  * @example
249
280
  * ```ts
250
- * import { createTLSchema, defaultShapeSchemas, defaultBindingSchemas } from '@tldraw/tlschema'
281
+ * import {
282
+ * createTLSchema,
283
+ * defaultShapeSchemas,
284
+ * defaultBindingSchemas,
285
+ * defaultAssetSchemas,
286
+ * } from '@tldraw/tlschema'
251
287
  * import { Store } from '@tldraw/store'
252
288
  *
253
- * // Create schema with all default shapes and bindings
289
+ * // Create schema with all default shapes, bindings, and assets
254
290
  * const schema = createTLSchema()
255
291
  *
256
292
  * // Create schema with custom shapes added
@@ -262,6 +298,8 @@ export interface UserSchemaInfo {
262
298
  * migrations: myCustomShapeMigrations,
263
299
  * },
264
300
  * },
301
+ * bindings: defaultBindingSchemas,
302
+ * assets: defaultAssetSchemas,
265
303
  * })
266
304
  *
267
305
  * // Create schema with custom user metadata
@@ -301,12 +339,14 @@ export interface UserSchemaInfo {
301
339
  export function createTLSchema({
302
340
  shapes = defaultShapeSchemas,
303
341
  bindings = defaultBindingSchemas,
342
+ assets = defaultAssetSchemas,
304
343
  user,
305
344
  records = {},
306
345
  migrations,
307
346
  }: {
308
347
  shapes?: Record<string, SchemaPropsInfo>
309
348
  bindings?: Record<string, SchemaPropsInfo>
349
+ assets?: Record<string, SchemaPropsInfo>
310
350
  user?: UserSchemaInfo
311
351
  records?: Record<string, CustomRecordInfo>
312
352
  migrations?: readonly MigrationSequence[]
@@ -323,6 +363,7 @@ export function createTLSchema({
323
363
 
324
364
  const ShapeRecordType = createShapeRecordType(shapes)
325
365
  const BindingRecordType = createBindingRecordType(bindings)
366
+ const _AssetRecordType = createAssetRecordType(assets)
326
367
  const InstanceRecordType = createInstanceRecordType(stylesById)
327
368
  const CustomUserRecordType = user ? createUserRecordType(user) : UserRecordType
328
369
 
@@ -353,7 +394,7 @@ export function createTLSchema({
353
394
 
354
395
  return StoreSchema.create(
355
396
  {
356
- asset: AssetRecordType,
397
+ asset: _AssetRecordType,
357
398
  binding: BindingRecordType,
358
399
  camera: CameraRecordType,
359
400
  document: DocumentRecordType,
@@ -380,10 +421,8 @@ export function createTLSchema({
380
421
  rootShapeMigrations,
381
422
 
382
423
  userMigrations,
383
- bookmarkAssetMigrations,
384
- imageAssetMigrations,
385
- videoAssetMigrations,
386
424
 
425
+ ...processPropsMigrations<TLUnknownAsset>('asset', assets),
387
426
  ...processPropsMigrations<TLShape>('shape', shapes),
388
427
  ...processPropsMigrations<TLBinding>('binding', bindings),
389
428
  ...processCustomRecordMigrations(records),
package/src/index.ts CHANGED
@@ -32,9 +32,13 @@
32
32
 
33
33
  import { registerTldrawLibraryVersion } from '@tldraw/utils'
34
34
  export { assetIdValidator, createAssetValidator, type TLBaseAsset } from './assets/TLBaseAsset'
35
- export { type TLBookmarkAsset } from './assets/TLBookmarkAsset'
36
- export { type TLImageAsset } from './assets/TLImageAsset'
37
- export { type TLVideoAsset } from './assets/TLVideoAsset'
35
+ export {
36
+ bookmarkAssetMigrations,
37
+ bookmarkAssetProps,
38
+ type TLBookmarkAsset,
39
+ } from './assets/TLBookmarkAsset'
40
+ export { imageAssetMigrations, imageAssetProps, type TLImageAsset } from './assets/TLImageAsset'
41
+ export { videoAssetMigrations, videoAssetProps, type TLVideoAsset } from './assets/TLVideoAsset'
38
42
  export {
39
43
  arrowBindingMigrations,
40
44
  arrowBindingProps,
@@ -56,6 +60,7 @@ export {
56
60
  } from './createPresenceStateDerivation'
57
61
  export {
58
62
  createTLSchema,
63
+ defaultAssetSchemas,
59
64
  defaultBindingSchemas,
60
65
  defaultShapeSchemas,
61
66
  type SchemaPropsInfo,
@@ -82,11 +87,17 @@ export { scribbleValidator, TL_SCRIBBLE_STATES, type TLScribble } from './misc/T
82
87
  export {
83
88
  assetMigrations,
84
89
  AssetRecordType,
85
- assetValidator,
90
+ createAssetPropsMigrationIds,
91
+ createAssetPropsMigrationSequence,
92
+ createAssetRecordType,
86
93
  type TLAsset,
87
94
  type TLAssetId,
88
95
  type TLAssetPartial,
89
96
  type TLAssetShape,
97
+ type TLDefaultAsset,
98
+ type TLGlobalAssetPropsMap,
99
+ type TLIndexedAssets,
100
+ type TLUnknownAsset,
90
101
  } from './records/TLAsset'
91
102
  export {
92
103
  createBindingId,
@@ -290,21 +301,18 @@ export {
290
301
  } from './shapes/TLVideoShape'
291
302
  export { EnumStyleProp, StyleProp, type StylePropValue } from './styles/StyleProp'
292
303
  export {
293
- defaultColorNames,
294
304
  DefaultColorStyle,
295
- DefaultColorThemePalette,
296
- DefaultLabelColorStyle,
297
- getColorValue,
298
- getDefaultColorTheme,
305
+ registerColorsFromThemes,
299
306
  type TLDefaultColorStyle,
300
- type TLDefaultColorTheme,
301
- type TLDefaultColorThemeColor,
302
307
  } from './styles/TLColorStyle'
303
308
  export { DefaultDashStyle, type TLDefaultDashStyle } from './styles/TLDashStyle'
304
309
  export { DefaultFillStyle, type TLDefaultFillStyle } from './styles/TLFillStyle'
310
+ export { type TLFontFace, type TLFontFaceSource } from './styles/TLFontFace'
305
311
  export {
306
312
  DefaultFontFamilies,
307
313
  DefaultFontStyle,
314
+ isFontEntry,
315
+ registerFontsFromThemes,
308
316
  type TLDefaultFontStyle,
309
317
  } from './styles/TLFontStyle'
310
318
  export {
@@ -313,6 +321,18 @@ export {
313
321
  } from './styles/TLHorizontalAlignStyle'
314
322
  export { DefaultSizeStyle, type TLDefaultSizeStyle } from './styles/TLSizeStyle'
315
323
  export { DefaultTextAlignStyle, type TLDefaultTextAlignStyle } from './styles/TLTextAlignStyle'
324
+ export {
325
+ type TLDefaultColor,
326
+ type TLRemovedDefaultThemeColors,
327
+ type TLTheme,
328
+ type TLThemeColors,
329
+ type TLThemeDefaultColors,
330
+ type TLThemeFont,
331
+ type TLThemeFonts,
332
+ type TLThemeId,
333
+ type TLThemes,
334
+ type TLThemeUiColorKeys,
335
+ } from './styles/TLTheme'
316
336
  export {
317
337
  DefaultVerticalAlignStyle,
318
338
  type TLDefaultVerticalAlignStyle,