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.
- package/dist/{create-app-QeUBrQ2M.js → create-app-d0GGTvpm.js} +49 -23
- package/dist/create-app-d0GGTvpm.js.map +1 -0
- package/dist/create-app.js +1 -1
- package/dist/index.js +87 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
- package/template/+express+azure-web-pubsub/fonts/Impact.ttf +0 -0
- package/template/+express+azure-web-pubsub/fonts/Verdana-Bold.ttf +0 -0
- package/template/+express+azure-web-pubsub/fonts/Verdana-BoldItalic.ttf +0 -0
- package/template/+express+azure-web-pubsub/fonts/Verdana-Italic.ttf +0 -0
- package/template/+express+azure-web-pubsub/fonts/Verdana.ttf +0 -0
- package/template/+express+azure-web-pubsub/fonts/inter-bold.ttf +0 -0
- package/template/+express+azure-web-pubsub/fonts/inter-italic-bold.ttf +0 -0
- package/template/+express+azure-web-pubsub/fonts/inter-italic.ttf +0 -0
- package/template/+express+azure-web-pubsub/fonts/inter-regular.ttf +0 -0
- package/template/+express+azure-web-pubsub/fonts/sansita-bold.ttf +0 -0
- package/template/+express+azure-web-pubsub/fonts/sansita-regular.ttf +0 -0
- package/template/+express+azure-web-pubsub/nodemon.json +6 -0
- package/template/+express+azure-web-pubsub/src/api/v1/controllers/getImage.ts +2 -2
- package/template/+express+azure-web-pubsub/src/api/v1/controllers/getRoom.ts +33 -0
- package/template/+express+azure-web-pubsub/src/api/v1/controllers/getRoomConnect.ts +1 -1
- package/template/+express+azure-web-pubsub/src/api/v1/controllers/postExportToImage.ts +98 -0
- package/template/+express+azure-web-pubsub/src/api/v1/controllers/postRemoveBackground.ts +1 -2
- package/template/+express+azure-web-pubsub/src/api/v1/controllers/workers/exportToImage.ts +52 -0
- package/template/+express+azure-web-pubsub/src/api/v1/controllers/workers/types.ts +5 -0
- package/template/+express+azure-web-pubsub/src/api/v1/router.ts +6 -2
- package/template/+express+azure-web-pubsub/src/canvas/fonts.ts +167 -0
- package/template/+express+azure-web-pubsub/src/canvas/nodes/color-token/color-token.ts +155 -0
- package/template/+express+azure-web-pubsub/src/canvas/types.ts +13 -0
- package/template/+express+azure-web-pubsub/src/canvas/weave.ts +207 -0
- package/template/+express+azure-web-pubsub/src/store.ts +9 -3
- package/template/+express+azure-web-pubsub/src/utils.ts +23 -0
- package/template/+express+azure-web-pubsub/src/workers/workers.ts +40 -0
- package/template/+express+azure-web-pubsub/tsconfig.json +1 -1
- package/template/+express+websockets/example.gitignore +5 -5
- package/template/+express+websockets/fonts/Impact.ttf +0 -0
- package/template/+express+websockets/fonts/Verdana-Bold.ttf +0 -0
- package/template/+express+websockets/fonts/Verdana-BoldItalic.ttf +0 -0
- package/template/+express+websockets/fonts/Verdana-Italic.ttf +0 -0
- package/template/+express+websockets/fonts/Verdana.ttf +0 -0
- package/template/+express+websockets/fonts/inter-bold.ttf +0 -0
- package/template/+express+websockets/fonts/inter-italic-bold.ttf +0 -0
- package/template/+express+websockets/fonts/inter-italic.ttf +0 -0
- package/template/+express+websockets/fonts/inter-regular.ttf +0 -0
- package/template/+express+websockets/fonts/sansita-bold.ttf +0 -0
- package/template/+express+websockets/fonts/sansita-regular.ttf +0 -0
- package/template/+express+websockets/nodemon.json +6 -0
- package/template/+express+websockets/src/api/v1/controllers/getImage.ts +2 -2
- package/template/+express+websockets/src/api/v1/controllers/getRoom.ts +33 -0
- package/template/+express+websockets/src/api/v1/controllers/postExportToImage.ts +96 -0
- package/template/+express+websockets/src/api/v1/controllers/postRemoveBackground.ts +1 -2
- package/template/+express+websockets/src/api/v1/controllers/workers/exportToImage.ts +48 -0
- package/template/+express+websockets/src/api/v1/controllers/workers/types.ts +1 -0
- package/template/+express+websockets/src/api/v1/router.ts +7 -1
- package/template/+express+websockets/src/canvas/fonts.ts +163 -0
- package/template/+express+websockets/src/canvas/nodes/color-token/color-token.ts +151 -0
- package/template/+express+websockets/src/canvas/types.ts +13 -0
- package/template/+express+websockets/src/canvas/weave.ts +203 -0
- package/template/+express+websockets/src/server.ts +4 -0
- package/template/+express+websockets/src/store.ts +2 -2
- package/template/+express+websockets/src/utils.ts +23 -0
- package/template/+express+websockets/src/workers/workers.ts +36 -0
- package/template/+express+websockets/tsconfig.json +1 -1
- package/template/package.json +7 -1
- 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
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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(`/
|
|
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
|
+
}
|