create-weave-backend-app 2.5.0 → 2.7.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.
Files changed (65) hide show
  1. package/dist/{create-app-QeUBrQ2M.js → create-app-d0GGTvpm.js} +49 -23
  2. package/dist/create-app-d0GGTvpm.js.map +1 -0
  3. package/dist/create-app.js +1 -1
  4. package/dist/index.js +87 -2
  5. package/dist/index.js.map +1 -1
  6. package/package.json +4 -1
  7. package/template/+express+azure-web-pubsub/fonts/Impact.ttf +0 -0
  8. package/template/+express+azure-web-pubsub/fonts/Verdana-Bold.ttf +0 -0
  9. package/template/+express+azure-web-pubsub/fonts/Verdana-BoldItalic.ttf +0 -0
  10. package/template/+express+azure-web-pubsub/fonts/Verdana-Italic.ttf +0 -0
  11. package/template/+express+azure-web-pubsub/fonts/Verdana.ttf +0 -0
  12. package/template/+express+azure-web-pubsub/fonts/inter-bold.ttf +0 -0
  13. package/template/+express+azure-web-pubsub/fonts/inter-italic-bold.ttf +0 -0
  14. package/template/+express+azure-web-pubsub/fonts/inter-italic.ttf +0 -0
  15. package/template/+express+azure-web-pubsub/fonts/inter-regular.ttf +0 -0
  16. package/template/+express+azure-web-pubsub/fonts/sansita-bold.ttf +0 -0
  17. package/template/+express+azure-web-pubsub/fonts/sansita-regular.ttf +0 -0
  18. package/template/+express+azure-web-pubsub/nodemon.json +6 -0
  19. package/template/+express+azure-web-pubsub/src/api/v1/controllers/getImage.ts +2 -2
  20. package/template/+express+azure-web-pubsub/src/api/v1/controllers/getRoom.ts +33 -0
  21. package/template/+express+azure-web-pubsub/src/api/v1/controllers/getRoomConnect.ts +1 -1
  22. package/template/+express+azure-web-pubsub/src/api/v1/controllers/postExportToImage.ts +98 -0
  23. package/template/+express+azure-web-pubsub/src/api/v1/controllers/postRemoveBackground.ts +1 -2
  24. package/template/+express+azure-web-pubsub/src/api/v1/controllers/workers/exportToImage.ts +52 -0
  25. package/template/+express+azure-web-pubsub/src/api/v1/controllers/workers/types.ts +5 -0
  26. package/template/+express+azure-web-pubsub/src/api/v1/router.ts +6 -2
  27. package/template/+express+azure-web-pubsub/src/canvas/fonts.ts +167 -0
  28. package/template/+express+azure-web-pubsub/src/canvas/nodes/color-token/color-token.ts +155 -0
  29. package/template/+express+azure-web-pubsub/src/canvas/types.ts +13 -0
  30. package/template/+express+azure-web-pubsub/src/canvas/weave.ts +207 -0
  31. package/template/+express+azure-web-pubsub/src/store.ts +9 -3
  32. package/template/+express+azure-web-pubsub/src/utils.ts +23 -0
  33. package/template/+express+azure-web-pubsub/src/workers/workers.ts +40 -0
  34. package/template/+express+azure-web-pubsub/tsconfig.json +1 -1
  35. package/template/+express+websockets/example.gitignore +5 -5
  36. package/template/+express+websockets/fonts/Impact.ttf +0 -0
  37. package/template/+express+websockets/fonts/Verdana-Bold.ttf +0 -0
  38. package/template/+express+websockets/fonts/Verdana-BoldItalic.ttf +0 -0
  39. package/template/+express+websockets/fonts/Verdana-Italic.ttf +0 -0
  40. package/template/+express+websockets/fonts/Verdana.ttf +0 -0
  41. package/template/+express+websockets/fonts/inter-bold.ttf +0 -0
  42. package/template/+express+websockets/fonts/inter-italic-bold.ttf +0 -0
  43. package/template/+express+websockets/fonts/inter-italic.ttf +0 -0
  44. package/template/+express+websockets/fonts/inter-regular.ttf +0 -0
  45. package/template/+express+websockets/fonts/sansita-bold.ttf +0 -0
  46. package/template/+express+websockets/fonts/sansita-regular.ttf +0 -0
  47. package/template/+express+websockets/nodemon.json +6 -0
  48. package/template/+express+websockets/src/api/v1/controllers/getImage.ts +2 -2
  49. package/template/+express+websockets/src/api/v1/controllers/getRoom.ts +33 -0
  50. package/template/+express+websockets/src/api/v1/controllers/postExportToImage.ts +96 -0
  51. package/template/+express+websockets/src/api/v1/controllers/postRemoveBackground.ts +1 -2
  52. package/template/+express+websockets/src/api/v1/controllers/workers/exportToImage.ts +48 -0
  53. package/template/+express+websockets/src/api/v1/controllers/workers/types.ts +1 -0
  54. package/template/+express+websockets/src/api/v1/router.ts +7 -1
  55. package/template/+express+websockets/src/canvas/fonts.ts +163 -0
  56. package/template/+express+websockets/src/canvas/nodes/color-token/color-token.ts +151 -0
  57. package/template/+express+websockets/src/canvas/types.ts +13 -0
  58. package/template/+express+websockets/src/canvas/weave.ts +203 -0
  59. package/template/+express+websockets/src/server.ts +4 -0
  60. package/template/+express+websockets/src/store.ts +2 -2
  61. package/template/+express+websockets/src/utils.ts +23 -0
  62. package/template/+express+websockets/src/workers/workers.ts +36 -0
  63. package/template/+express+websockets/tsconfig.json +1 -1
  64. package/template/package.json +7 -1
  65. package/dist/create-app-QeUBrQ2M.js.map +0 -1
@@ -0,0 +1,40 @@
1
+ // SPDX-FileCopyrightText: 2025 2025 INDUSTRIA DE DISEÑO TEXTIL S.A. (INDITEX S.A.)
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+
5
+ import { cpus } from 'os'
6
+ import { Worker } from 'worker_threads'
7
+
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ let queue: any = null
10
+
11
+ export const setupWorkers = async () => {
12
+ const PQueue = await import('p-queue')
13
+ queue = new PQueue.default({ concurrency: cpus().length })
14
+ }
15
+
16
+ export function runWorker<T>(
17
+ workerPath: string,
18
+ workerData?: unknown
19
+ ): Promise<void | T> {
20
+ if (!queue) {
21
+ throw new Error('Workers not initialized. Call setupWorkers() first.')
22
+ }
23
+
24
+ return queue.add(async () => {
25
+ const worker = new Worker(workerPath)
26
+
27
+ const result = await new Promise<T>((resolve, reject) => {
28
+ worker.on('message', resolve)
29
+ worker.on('error', reject)
30
+ worker.on('exit', (code) => {
31
+ if (code !== 0) {
32
+ reject(new Error(`Worker stopped with exit code ${code}`))
33
+ }
34
+ })
35
+ worker.postMessage(workerData)
36
+ })
37
+
38
+ return result
39
+ })
40
+ }
@@ -3,7 +3,7 @@
3
3
  "target": "es2020",
4
4
  "module": "NodeNext",
5
5
  "rootDir": "./src",
6
- "moduleResolution": "nodenext",
6
+ "moduleResolution": "NodeNext",
7
7
  "outDir": "./dist",
8
8
  "esModuleInterop": true,
9
9
  "forceConsistentCasingInFileNames": true,
@@ -30,8 +30,8 @@ vite.config.ts*
30
30
  .env*
31
31
  .npmrc
32
32
 
33
- images
34
- images-mimetype
35
- rooms
36
- temp
37
- public
33
+ /images/
34
+ /images-mimetype/
35
+ /rooms
36
+ /temp
37
+ /public
@@ -0,0 +1,6 @@
1
+ {
2
+ "watch": ["src"],
3
+ "ext": "ts,js,json",
4
+ "ignore": ["dist"],
5
+ "exec": "npm run build && npm run copy:assets && npm run copy:fonts && npm run start:dev"
6
+ }
@@ -18,8 +18,8 @@ export const getImageController = () => {
18
18
  const filePath = await persistenceHandler.getFilePath(fileName)
19
19
 
20
20
  if (filePath) {
21
- const mimeType = await persistenceHandler.getMimeType(fileName)
22
- res.setHeader('Content-Type', mimeType)
21
+ // const mimeType = await persistenceHandler.getMimeType(fileName)
22
+ // res.setHeader('Content-Type', mimeType)
23
23
  res.sendFile(filePath, (err) => {
24
24
  if (err) {
25
25
  console.error('File not found or error sending file:', err.message)
@@ -0,0 +1,33 @@
1
+ import path from 'path'
2
+ import { fileURLToPath } from 'node:url'
3
+ import { Request, Response } from 'express'
4
+ import { createFolder, existsFolder, existFile } from '@/utils.js'
5
+
6
+ const __filename = fileURLToPath(import.meta.url)
7
+ const __dirname = path.dirname(__filename)
8
+
9
+ export const getRoomController =
10
+ () =>
11
+ async (req: Request, res: Response): Promise<void> => {
12
+ const roomId = req.params.roomId
13
+
14
+ try {
15
+ const roomsFolder = path.join(__dirname, '..', '..', '..', 'rooms')
16
+
17
+ if (!(await existsFolder(roomsFolder))) {
18
+ await createFolder(roomsFolder)
19
+ }
20
+
21
+ const roomsFile = path.join(roomsFolder, roomId)
22
+
23
+ if (!(await existFile(roomsFile))) {
24
+ res.status(404).json({ status: 'KO', message: 'Room not found' })
25
+ return
26
+ }
27
+
28
+ res.download(roomsFile)
29
+ } catch (error) {
30
+ console.log(error)
31
+ res.status(500).json({ status: 'KO', message: 'Error fetching the room' })
32
+ }
33
+ }
@@ -0,0 +1,96 @@
1
+ import path from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+ import { z } from 'zod'
4
+ import { Request, Response } from 'express'
5
+ import archiver from 'archiver'
6
+ import { WeaveExportFormats } from '@inditextech/weave-types'
7
+ import { getServiceConfig } from '../../../config/config.js'
8
+ import { runWorker } from '../../../workers/workers.js'
9
+ import { ExportToImageWorkerResult } from './workers/types.js'
10
+
11
+ const __filename = fileURLToPath(import.meta.url)
12
+ const __dirname = path.dirname(__filename)
13
+
14
+ const WeaveExportFormatsSchema: z.ZodType<WeaveExportFormats> = z.enum([
15
+ 'image/png',
16
+ 'image/jpeg'
17
+ ])
18
+
19
+ const payloadSchema = z.object({
20
+ roomData: z.string().base64(),
21
+ nodes: z.array(z.string()).optional().default([]),
22
+ options: z.object({
23
+ format: WeaveExportFormatsSchema.optional().default('image/png'),
24
+ backgroundColor: z.string().optional().default('transparent'),
25
+ padding: z.number().min(0).optional().default(20),
26
+ pixelRatio: z.number().min(1).optional().default(1),
27
+ quality: z.number().min(0).max(1).optional().default(1)
28
+ }),
29
+ responseType: z.enum(['base64', 'blob', 'zip']).optional().default('blob')
30
+ })
31
+
32
+ export const postExportToImageController = () => {
33
+ const config = getServiceConfig()
34
+
35
+ return async (req: Request, res: Response): Promise<void> => {
36
+ const parsedBody = payloadSchema.safeParse(req.body)
37
+
38
+ if (!parsedBody.success) {
39
+ res.status(400).json({ errors: parsedBody.error.errors })
40
+ return
41
+ }
42
+
43
+ try {
44
+ console.log(__dirname)
45
+ const result = await runWorker<ExportToImageWorkerResult>(
46
+ path.join(__dirname, './workers/exportToImage.js'),
47
+ {
48
+ ...parsedBody.data,
49
+ config
50
+ }
51
+ )
52
+
53
+ const finalBuffer = result as Buffer
54
+
55
+ // success
56
+ const fileExtension =
57
+ parsedBody.data.options.format.split('/')[1] === 'png' ? '.png' : '.jpg'
58
+
59
+ if (parsedBody.data.responseType === 'zip') {
60
+ res.setHeader('Content-Type', 'application/zip')
61
+ res.setHeader('Content-Disposition', 'attachment; filename=export.zip')
62
+
63
+ const archive = archiver('zip', { zlib: { level: 9 } })
64
+ archive.pipe(res)
65
+
66
+ archive.append(Buffer.from(finalBuffer), {
67
+ name: `render${fileExtension}`
68
+ })
69
+
70
+ // Finalize ZIP
71
+ archive.finalize()
72
+ return
73
+ }
74
+
75
+ if (parsedBody.data.responseType === 'blob') {
76
+ res.setHeader('Content-Type', 'application/octet-stream')
77
+ res.setHeader(
78
+ 'Content-Disposition',
79
+ `attachment; filename="render${fileExtension}"`
80
+ )
81
+
82
+ res.status(200).send(Buffer.from(finalBuffer))
83
+ return
84
+ }
85
+
86
+ res.status(200).json({
87
+ url: `data:${parsedBody.data.options.format};base64,${Buffer.from(
88
+ finalBuffer
89
+ ).toString('base64')}`
90
+ })
91
+ } catch (error) {
92
+ console.log(error)
93
+ res.status(500).json({ error: 'Error processing image' })
94
+ }
95
+ }
96
+ }
@@ -1,4 +1,3 @@
1
- import fs from 'node:fs'
2
1
  import path from 'node:path'
3
2
  import { Request, Response } from 'express'
4
3
  import { removeBackground } from '@imgly/background-removal-node'
@@ -36,7 +35,7 @@ export const postRemoveBackgroundController = () => {
36
35
  const data = await myBlobToUIntDemo(blob)
37
36
  const fileNameRemoved = `${fileName}-removed`
38
37
  await persistenceHandler.persist(fileNameRemoved, 'image/png', data)
39
- fs.rmSync(filePathDownload)
38
+ // fs.rmSync(filePathDownload)
40
39
 
41
40
  res.status(201).json({
42
41
  status: 'Image created OK',
@@ -0,0 +1,48 @@
1
+ import { parentPort } from 'worker_threads'
2
+ import sharp from 'sharp'
3
+ import { WEAVE_EXPORT_FORMATS } from '@inditextech/weave-types'
4
+ import { renderWeaveRoom } from '../../../../canvas/weave.js'
5
+
6
+ parentPort?.on('message', async ({ config, roomData, nodes, options }) => {
7
+ const { instance, destroy } = await renderWeaveRoom(config, roomData)
8
+
9
+ const { composites, width, height } = await instance.exportNodesServerSide(
10
+ nodes,
11
+ (nodes) => nodes,
12
+ {
13
+ format: options.format,
14
+ padding: options.padding,
15
+ pixelRatio: options.pixelRatio,
16
+ backgroundColor: options.backgroundColor,
17
+ quality: options.quality
18
+ }
19
+ )
20
+
21
+ destroy()
22
+
23
+ try {
24
+ const composedImage = sharp({
25
+ create: {
26
+ width,
27
+ height,
28
+ channels: 4,
29
+ background: { r: 0, g: 0, b: 0, alpha: 0 }
30
+ }
31
+ }).composite(composites)
32
+
33
+ let imagePipeline = composedImage
34
+ if (options.format === WEAVE_EXPORT_FORMATS.JPEG) {
35
+ imagePipeline = composedImage.jpeg({
36
+ quality: (options.quality ?? 0.8) * 100
37
+ })
38
+ }
39
+ if (options.format === WEAVE_EXPORT_FORMATS.PNG) {
40
+ imagePipeline = composedImage.png()
41
+ }
42
+
43
+ const imageBuffer = await imagePipeline.toBuffer()
44
+ parentPort?.postMessage(imageBuffer, [imageBuffer.buffer as ArrayBuffer])
45
+ } catch (error) {
46
+ parentPort?.postMessage((error as Error).message)
47
+ }
48
+ })
@@ -0,0 +1 @@
1
+ export type ExportToImageWorkerResult = Buffer
@@ -7,6 +7,8 @@ import { postUploadImageController } from './controllers/postUploadImage.js'
7
7
  import { delImageController } from './controllers/delImage.js'
8
8
  import { getImagesController } from './controllers/getImages.js'
9
9
  import { postRemoveBackgroundController } from './controllers/postRemoveBackground.js'
10
+ import { postExportToImageController } from './controllers/postExportToImage.js'
11
+ import { getRoomController } from './controllers/getRoom.js'
10
12
 
11
13
  const router: Router = Router()
12
14
 
@@ -24,7 +26,8 @@ export function setupApiV1Router(app: Express) {
24
26
  router.get(`/health`, getHealthController())
25
27
 
26
28
  // Room handling API
27
- router.get(`/sync/rooms/:roomId`, getRoomConnectController())
29
+ router.get(`/rooms/:roomId`, getRoomController())
30
+ router.get(`/rooms/:roomId/connect`, getRoomConnectController())
28
31
 
29
32
  // Images handling API
30
33
  router.get(`/rooms/:roomId/images`, getImagesController())
@@ -40,5 +43,8 @@ export function setupApiV1Router(app: Express) {
40
43
  )
41
44
  router.delete(`/rooms/:roomId/images/:imageId`, delImageController())
42
45
 
46
+ // Render Canvas API
47
+ router.post(`/export`, postExportToImageController())
48
+
43
49
  app.use('/api/v1', router)
44
50
  }
@@ -0,0 +1,163 @@
1
+ import path from 'node:path'
2
+ import { registerFont } from 'canvas'
3
+ import { FontLibrary } from 'skia-canvas'
4
+ import { CanvasFont, SkiaFont } from './types.js'
5
+
6
+ let registered = false
7
+
8
+ export const registerSkiaFonts = () => {
9
+ if (registered) {
10
+ return
11
+ }
12
+
13
+ FontLibrary.reset()
14
+
15
+ const fonts: SkiaFont[] = [
16
+ // Arial font family
17
+ {
18
+ family: 'Arial',
19
+ paths: [
20
+ path.resolve(process.cwd(), 'fonts/ARIALI.ttf'),
21
+ path.resolve(process.cwd(), 'fonts/ARIAL.ttf')
22
+ ]
23
+ },
24
+ // NotoSansMono font family
25
+ {
26
+ family: 'NotoSansMono',
27
+ // paths: [path.resolve(process.cwd(), "fonts/NotoSansMono-Italic.ttf")],
28
+ paths: [path.resolve(process.cwd(), 'fonts/NotoSansMono-Regular.ttf')]
29
+ },
30
+ // Impact font family
31
+ {
32
+ family: 'Impact',
33
+ paths: [path.resolve(process.cwd(), 'fonts/Impact.ttf')]
34
+ },
35
+ // Verdana font family
36
+ {
37
+ family: 'Verdana',
38
+ paths: [
39
+ path.resolve(process.cwd(), 'fonts/Verdana-Italic.ttf'),
40
+ path.resolve(process.cwd(), 'fonts/Verdana-Bold.ttf'),
41
+ path.resolve(process.cwd(), 'fonts/Verdana-BoldItalic.ttf'),
42
+ path.resolve(process.cwd(), 'fonts/Verdana.ttf')
43
+ ]
44
+ },
45
+ // Inter font family
46
+ {
47
+ family: 'Inter',
48
+ paths: [
49
+ path.resolve(process.cwd(), 'fonts/inter-bold.ttf'),
50
+ path.resolve(process.cwd(), 'fonts/inter-italic.ttf'),
51
+ path.resolve(process.cwd(), 'fonts/inter-italic-bold.ttf'),
52
+ path.resolve(process.cwd(), 'fonts/inter-regular.ttf')
53
+ ]
54
+ },
55
+ // Sansita font family
56
+ {
57
+ family: 'Sansita',
58
+ paths: [
59
+ path.resolve(process.cwd(), 'fonts/sansita-bold.ttf'),
60
+ path.resolve(process.cwd(), 'fonts/sansita-regular.ttf')
61
+ ]
62
+ }
63
+ ]
64
+
65
+ for (const font of fonts) {
66
+ FontLibrary.use(font.family, font.paths)
67
+ }
68
+
69
+ registered = true
70
+ }
71
+
72
+ export const registerCanvasFonts = () => {
73
+ const fonts: CanvasFont[] = [
74
+ // Impact font family
75
+ {
76
+ path: path.resolve(process.cwd(), 'fonts/Impact.ttf'),
77
+ fontFace: {
78
+ family: 'Impact',
79
+ weight: '400',
80
+ style: 'normal'
81
+ }
82
+ },
83
+ // Verdana font family
84
+ {
85
+ path: path.resolve(process.cwd(), 'fonts/Verdana.ttf'),
86
+ fontFace: {
87
+ family: 'Verdana',
88
+ weight: '400',
89
+ style: 'normal'
90
+ }
91
+ },
92
+ {
93
+ path: path.resolve(process.cwd(), 'fonts/Verdana-Bold.ttf'),
94
+ fontFace: {
95
+ family: 'Verdana',
96
+ weight: '700',
97
+ style: 'normal'
98
+ }
99
+ },
100
+ {
101
+ path: path.resolve(process.cwd(), 'fonts/Verdana-Italic.ttf'),
102
+ fontFace: {
103
+ family: 'Verdana',
104
+ weight: '400',
105
+ style: 'italic'
106
+ }
107
+ },
108
+ {
109
+ path: path.resolve(process.cwd(), 'fonts/Verdana-BoldItalic.ttf'),
110
+ fontFace: {
111
+ family: 'Verdana',
112
+ weight: '700',
113
+ style: 'italic'
114
+ }
115
+ },
116
+ // Inter font family
117
+ {
118
+ path: path.resolve(process.cwd(), 'fonts/inter-regular.ttf'),
119
+ fontFace: {
120
+ family: 'Inter',
121
+ weight: '400',
122
+ style: 'normal'
123
+ }
124
+ },
125
+ {
126
+ path: path.resolve(process.cwd(), 'fonts/inter-bold.ttf'),
127
+ fontFace: {
128
+ family: 'Inter',
129
+ weight: '700',
130
+ style: 'normal'
131
+ }
132
+ },
133
+ {
134
+ path: path.resolve(process.cwd(), 'fonts/inter-italic.ttf'),
135
+ fontFace: {
136
+ family: 'Inter',
137
+ weight: '400',
138
+ style: 'italic'
139
+ }
140
+ },
141
+ {
142
+ path: path.resolve(process.cwd(), 'fonts/inter-italic-bold.ttf'),
143
+ fontFace: {
144
+ family: 'Inter',
145
+ weight: '700',
146
+ style: 'italic'
147
+ }
148
+ },
149
+ // Sansita font family
150
+ {
151
+ path: path.resolve(process.cwd(), 'fonts/sansita-regular.ttf'),
152
+ fontFace: {
153
+ family: 'Sansita',
154
+ weight: '400',
155
+ style: 'normal'
156
+ }
157
+ }
158
+ ]
159
+
160
+ for (const font of fonts) {
161
+ registerFont(font.path, font.fontFace)
162
+ }
163
+ }
@@ -0,0 +1,151 @@
1
+ import Konva from 'konva'
2
+ import { WeaveNode } from '@inditextech/weave-sdk/server'
3
+ import {
4
+ WeaveElementAttributes,
5
+ WeaveElementInstance
6
+ } from '@inditextech/weave-types'
7
+
8
+ export const COLOR_TOKEN_NODE_TYPE = 'color-token'
9
+
10
+ export class ColorTokenNode extends WeaveNode {
11
+ protected nodeType = COLOR_TOKEN_NODE_TYPE
12
+
13
+ onRender(props: WeaveElementAttributes) {
14
+ const { id } = props
15
+
16
+ const colorTokenColor = props.colorToken ?? '#DEFFA0'
17
+
18
+ const colorTokenParams = {
19
+ ...props
20
+ }
21
+ delete colorTokenParams.zIndex
22
+
23
+ const colorTokenNode = new Konva.Group({
24
+ ...colorTokenParams,
25
+ width: colorTokenParams.width,
26
+ height: colorTokenParams.height,
27
+ name: 'node'
28
+ })
29
+
30
+ this.setupDefaultNodeAugmentation(colorTokenNode)
31
+
32
+ const internalRect = new Konva.Rect({
33
+ groupId: id,
34
+ nodeId: id,
35
+ id: `${id}-colorToken`,
36
+ x: 0,
37
+ y: 0,
38
+ fill: '#FFFFFFFF',
39
+ width: colorTokenParams.width,
40
+ height: colorTokenParams.height,
41
+ strokeEnabled: false
42
+ })
43
+
44
+ colorTokenNode.add(internalRect)
45
+
46
+ const internalRect2 = new Konva.Rect({
47
+ id: `${id}-colorToken-1`,
48
+ groupId: id,
49
+ nodeId: id,
50
+ x: 0,
51
+ y: 0,
52
+ fill: colorTokenColor,
53
+ strokeWidth: 0,
54
+ strokeEnabled: false,
55
+ width: colorTokenParams.width,
56
+ height: (colorTokenParams.height ?? 0) - 60,
57
+ listening: false,
58
+ draggable: false
59
+ })
60
+
61
+ colorTokenNode.add(internalRect2)
62
+
63
+ const internalText = new Konva.Text({
64
+ id: `${id}-colorToken-code`,
65
+ groupId: id,
66
+ nodeId: id,
67
+ x: 20,
68
+ y: 260,
69
+ fontSize: 20,
70
+ fontFamily: 'Inter, sans-serif',
71
+ fill: '#CCCCCCFF',
72
+ strokeEnabled: false,
73
+ stroke: '#000000FF',
74
+ strokeWidth: 1,
75
+ text: `${colorTokenColor}`,
76
+ width: (colorTokenParams.width ?? 0) - 40,
77
+ height: 20,
78
+ align: 'left',
79
+ listening: false,
80
+ draggable: false
81
+ })
82
+
83
+ colorTokenNode.add(internalText)
84
+
85
+ const border = new Konva.Rect({
86
+ groupId: id,
87
+ nodeId: id,
88
+ id: `${id}-colorToken-border`,
89
+ x: 0,
90
+ y: 0,
91
+ fill: 'transparent',
92
+ width: colorTokenParams.width,
93
+ height: colorTokenParams.height,
94
+ strokeScaleEnabled: true,
95
+ stroke: 'black',
96
+ strokeWidth: 1
97
+ })
98
+
99
+ colorTokenNode.add(border)
100
+ border.moveToTop()
101
+
102
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
103
+ ;(colorTokenNode as any).getTransformerProperties = () => {
104
+ const baseConfig = this.defaultGetTransformerProperties({})
105
+ return {
106
+ ...baseConfig,
107
+ resizeEnabled: false,
108
+ enabledAnchors: [] as string[],
109
+ borderStrokeWidth: 2,
110
+ padding: 0
111
+ }
112
+ }
113
+
114
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
115
+ ;(colorTokenNode as any).allowedAnchors = () => {
116
+ return []
117
+ }
118
+
119
+ this.setupDefaultNodeEvents(colorTokenNode)
120
+
121
+ return colorTokenNode
122
+ }
123
+
124
+ onUpdate(
125
+ nodeInstance: WeaveElementInstance,
126
+ nextProps: WeaveElementAttributes
127
+ ) {
128
+ const { id, colorToken } = nextProps
129
+
130
+ const colorTokenNode = nodeInstance as Konva.Group
131
+
132
+ const nodeInstanceZIndex = nodeInstance.zIndex()
133
+ nodeInstance.setAttrs({
134
+ ...nextProps,
135
+ zIndex: nodeInstanceZIndex
136
+ })
137
+
138
+ const colorTokenColor = colorToken ?? '#DEFFA0'
139
+
140
+ const colorTokenNode1 = colorTokenNode.findOne(`#${id}-colorToken-1`)
141
+ if (colorTokenNode1) {
142
+ colorTokenNode1.setAttrs({
143
+ fill: colorTokenColor
144
+ })
145
+ }
146
+ const colorTokenCode = colorTokenNode.findOne(`#${id}-colorToken-code`)
147
+ if (colorTokenCode) {
148
+ colorTokenCode.setAttr('text', `${colorTokenColor}`)
149
+ }
150
+ }
151
+ }
@@ -0,0 +1,13 @@
1
+ export type SkiaFont = {
2
+ family: string
3
+ paths: string[]
4
+ }
5
+
6
+ export type CanvasFont = {
7
+ path: string
8
+ fontFace: {
9
+ family: string
10
+ weight?: string | undefined
11
+ style?: string | undefined
12
+ }
13
+ }