@canva/cli 1.8.0 → 1.10.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/CHANGELOG.md +22 -0
- package/cli.js +266 -366
- package/lib/cjs/index.cjs +6 -6
- package/lib/esm/index.mjs +6 -6
- package/package.json +1 -1
- package/templates/base/backend/database/database.ts +2 -2
- package/templates/base/backend/routers/auth.ts +5 -5
- package/templates/base/backend/routers/oauth.ts +4 -4
- package/templates/base/package.json +1 -2
- package/templates/base/scripts/copy_env.ts +2 -2
- package/templates/base/scripts/ssl/ssl.ts +3 -3
- package/templates/base/scripts/start/app_runner.ts +29 -7
- package/templates/base/scripts/start/context.ts +2 -2
- package/templates/base/scripts/start/start.ts +1 -1
- package/templates/base/scripts/start.tests.ts +1 -1
- package/templates/base/tsconfig.json +7 -4
- package/templates/base/utils/backend/base_backend/create.ts +7 -7
- package/templates/base/utils/backend/bearer_middleware/bearer_middleware.ts +3 -5
- package/templates/base/utils/backend/jwt_middleware/jwt_middleware.ts +5 -10
- package/templates/base/utils/use_add_element.ts +10 -0
- package/templates/base/webpack.config.ts +4 -4
- package/templates/common/utils/backend/base_backend/create.ts +7 -7
- package/templates/common/utils/backend/jwt_middleware/jwt_middleware.ts +5 -10
- package/templates/common/utils/table_wrapper.ts +80 -37
- package/templates/common/utils/use_add_element.ts +10 -0
- package/templates/common/utils/use_overlay_hook.ts +6 -4
- package/templates/dam/backend/routers/dam.ts +37 -19
- package/templates/dam/backend/server.ts +2 -2
- package/templates/dam/package.json +1 -2
- package/templates/dam/scripts/copy_env.ts +2 -2
- package/templates/dam/scripts/ssl/ssl.ts +3 -3
- package/templates/dam/scripts/start/app_runner.ts +29 -7
- package/templates/dam/scripts/start/context.ts +2 -2
- package/templates/dam/scripts/start/start.ts +1 -1
- package/templates/dam/tsconfig.json +7 -4
- package/templates/dam/utils/backend/base_backend/create.ts +7 -7
- package/templates/dam/utils/backend/jwt_middleware/jwt_middleware.ts +5 -10
- package/templates/dam/webpack.config.ts +4 -4
- package/templates/data_connector/package.json +2 -2
- package/templates/data_connector/scripts/copy_env.ts +2 -2
- package/templates/data_connector/scripts/ssl/ssl.ts +3 -3
- package/templates/data_connector/scripts/start/app_runner.ts +29 -7
- package/templates/data_connector/scripts/start/context.ts +2 -2
- package/templates/data_connector/scripts/start/start.ts +1 -1
- package/templates/data_connector/src/utils/data_table.ts +2 -1
- package/templates/data_connector/src/utils/tests/data_table.test.ts +2 -2
- package/templates/data_connector/tsconfig.json +7 -4
- package/templates/data_connector/webpack.config.ts +4 -4
- package/templates/gen_ai/backend/database/database.ts +2 -2
- package/templates/gen_ai/backend/routers/image.ts +5 -7
- package/templates/gen_ai/backend/server.ts +2 -2
- package/templates/gen_ai/package.json +2 -2
- package/templates/gen_ai/scripts/copy_env.ts +2 -2
- package/templates/gen_ai/scripts/ssl/ssl.ts +3 -3
- package/templates/gen_ai/scripts/start/app_runner.ts +29 -7
- package/templates/gen_ai/scripts/start/context.ts +2 -2
- package/templates/gen_ai/scripts/start/start.ts +1 -1
- package/templates/gen_ai/src/components/image_grid.tsx +6 -2
- package/templates/gen_ai/src/components/prompt_input.tsx +4 -1
- package/templates/gen_ai/tsconfig.json +7 -4
- package/templates/gen_ai/utils/backend/base_backend/create.ts +7 -7
- package/templates/gen_ai/utils/backend/bearer_middleware/bearer_middleware.ts +3 -5
- package/templates/gen_ai/webpack.config.ts +4 -4
- package/templates/hello_world/package.json +2 -2
- package/templates/hello_world/scripts/copy_env.ts +2 -2
- package/templates/hello_world/scripts/ssl/ssl.ts +3 -3
- package/templates/hello_world/scripts/start/app_runner.ts +29 -7
- package/templates/hello_world/scripts/start/context.ts +2 -2
- package/templates/hello_world/scripts/start/start.ts +1 -1
- package/templates/hello_world/src/tests/app.tests.tsx +1 -1
- package/templates/hello_world/tsconfig.json +7 -4
- package/templates/hello_world/utils/use_add_element.ts +10 -0
- package/templates/hello_world/webpack.config.ts +4 -4
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import debug from "debug";
|
|
3
|
-
import
|
|
3
|
+
import express from "express";
|
|
4
4
|
import type { NextFunction, Request, Response } from "express";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import http from "http";
|
|
7
|
+
import https from "https";
|
|
8
8
|
|
|
9
9
|
const serverDebug = debug("server");
|
|
10
10
|
|
|
@@ -41,12 +41,12 @@ export function createBaseServer(router: express.Router): BaseServer {
|
|
|
41
41
|
app.disable("x-powered-by");
|
|
42
42
|
|
|
43
43
|
// Health check endpoint
|
|
44
|
-
app.get("/healthz", (req, res
|
|
44
|
+
app.get("/healthz", (req, res) => {
|
|
45
45
|
res.sendStatus(200);
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
// logging middleware
|
|
49
|
-
app.use((req
|
|
49
|
+
app.use((req, _res, next) => {
|
|
50
50
|
serverDebug(`${new Date().toISOString()}: ${req.method} ${req.url}`);
|
|
51
51
|
next();
|
|
52
52
|
});
|
|
@@ -62,7 +62,7 @@ export function createBaseServer(router: express.Router): BaseServer {
|
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
// default error handler
|
|
65
|
-
app.use((err,
|
|
65
|
+
app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
|
|
66
66
|
console.error(err.stack);
|
|
67
67
|
res.status(500).send({
|
|
68
68
|
error: "something went wrong",
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
|
-
import
|
|
2
|
+
import debug from "debug";
|
|
3
3
|
import type { NextFunction, Request, Response } from "express";
|
|
4
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5
|
-
import Express from "express-serve-static-core";
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* Prefix your start command with `DEBUG=express:middleware:bearer` to enable debug logging
|
|
@@ -47,13 +45,13 @@ export function createBearerMiddleware(
|
|
|
47
45
|
|
|
48
46
|
req["user_id"] = user;
|
|
49
47
|
|
|
50
|
-
next();
|
|
48
|
+
return next();
|
|
51
49
|
} catch (e) {
|
|
52
50
|
if (e instanceof AuthorizationError) {
|
|
53
51
|
return sendUnauthorizedResponse(res, e.message);
|
|
54
52
|
}
|
|
55
53
|
|
|
56
|
-
next(e);
|
|
54
|
+
return next(e);
|
|
57
55
|
}
|
|
58
56
|
};
|
|
59
57
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
|
-
import
|
|
3
|
-
import * as debug from "debug";
|
|
2
|
+
import debug from "debug";
|
|
4
3
|
import type { NextFunction, Request, Response } from "express";
|
|
5
|
-
|
|
6
|
-
import Express from "express-serve-static-core";
|
|
7
|
-
import * as jwt from "jsonwebtoken";
|
|
4
|
+
import jwt from "jsonwebtoken";
|
|
8
5
|
import { JwksClient, SigningKeyNotFoundError } from "jwks-rsa";
|
|
9
6
|
|
|
10
7
|
/**
|
|
@@ -116,7 +113,7 @@ export function createJwtMiddleware(
|
|
|
116
113
|
userId: payload.userId,
|
|
117
114
|
};
|
|
118
115
|
|
|
119
|
-
next();
|
|
116
|
+
return next();
|
|
120
117
|
} catch (e) {
|
|
121
118
|
if (e instanceof JWTAuthorizationError) {
|
|
122
119
|
return sendUnauthorizedResponse(res, e.message);
|
|
@@ -125,9 +122,7 @@ export function createJwtMiddleware(
|
|
|
125
122
|
if (e instanceof SigningKeyNotFoundError) {
|
|
126
123
|
return sendUnauthorizedResponse(
|
|
127
124
|
res,
|
|
128
|
-
`Public key not found.
|
|
129
|
-
"Ensure you have the correct App_ID set",
|
|
130
|
-
)}.`,
|
|
125
|
+
`Public key not found. Ensure you have the correct App_ID set`,
|
|
131
126
|
);
|
|
132
127
|
}
|
|
133
128
|
|
|
@@ -139,7 +134,7 @@ export function createJwtMiddleware(
|
|
|
139
134
|
return sendUnauthorizedResponse(res, "Token expired");
|
|
140
135
|
}
|
|
141
136
|
|
|
142
|
-
next(e);
|
|
137
|
+
return next(e);
|
|
143
138
|
}
|
|
144
139
|
};
|
|
145
140
|
}
|
|
@@ -30,6 +30,11 @@ export const useAddElement = () => {
|
|
|
30
30
|
} else if (features.isSupported(addElementAtCursor)) {
|
|
31
31
|
return addElementAtCursor(element);
|
|
32
32
|
}
|
|
33
|
+
// eslint-disable-next-line no-console
|
|
34
|
+
console.warn(
|
|
35
|
+
"Neither addElementAtPoint nor addElementAtCursor are supported",
|
|
36
|
+
);
|
|
37
|
+
return Promise.resolve();
|
|
33
38
|
};
|
|
34
39
|
});
|
|
35
40
|
|
|
@@ -40,6 +45,11 @@ export const useAddElement = () => {
|
|
|
40
45
|
} else if (isSupported(addElementAtCursor)) {
|
|
41
46
|
return addElementAtCursor(element);
|
|
42
47
|
}
|
|
48
|
+
// eslint-disable-next-line no-console
|
|
49
|
+
console.warn(
|
|
50
|
+
"Neither addElementAtPoint nor addElementAtCursor are supported",
|
|
51
|
+
);
|
|
52
|
+
return Promise.resolve();
|
|
43
53
|
};
|
|
44
54
|
setAddElement(() => addElement);
|
|
45
55
|
}, [isSupported]);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Configuration } from "webpack";
|
|
2
2
|
import { DefinePlugin, optimize } from "webpack";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import path from "path";
|
|
4
|
+
import TerserPlugin from "terser-webpack-plugin";
|
|
5
5
|
import { transform } from "@formatjs/ts-transformer";
|
|
6
|
-
import
|
|
6
|
+
import chalk from "chalk";
|
|
7
7
|
import { config } from "dotenv";
|
|
8
8
|
import { Configuration as DevServerConfiguration } from "webpack-dev-server";
|
|
9
9
|
|
|
@@ -196,7 +196,7 @@ function buildDevConfig(options?: DevConfig): {
|
|
|
196
196
|
return {};
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
const { port, enableHmr, appOrigin,
|
|
199
|
+
const { port, enableHmr, appOrigin, enableHttps, certFile, keyFile } =
|
|
200
200
|
options;
|
|
201
201
|
const host = "localhost";
|
|
202
202
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import debug from "debug";
|
|
3
|
-
import
|
|
3
|
+
import express from "express";
|
|
4
4
|
import type { NextFunction, Request, Response } from "express";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import http from "http";
|
|
7
|
+
import https from "https";
|
|
8
8
|
|
|
9
9
|
const serverDebug = debug("server");
|
|
10
10
|
|
|
@@ -41,12 +41,12 @@ export function createBaseServer(router: express.Router): BaseServer {
|
|
|
41
41
|
app.disable("x-powered-by");
|
|
42
42
|
|
|
43
43
|
// Health check endpoint
|
|
44
|
-
app.get("/healthz", (req, res
|
|
44
|
+
app.get("/healthz", (req, res) => {
|
|
45
45
|
res.sendStatus(200);
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
// logging middleware
|
|
49
|
-
app.use((req
|
|
49
|
+
app.use((req, _res, next) => {
|
|
50
50
|
serverDebug(`${new Date().toISOString()}: ${req.method} ${req.url}`);
|
|
51
51
|
next();
|
|
52
52
|
});
|
|
@@ -62,7 +62,7 @@ export function createBaseServer(router: express.Router): BaseServer {
|
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
// default error handler
|
|
65
|
-
app.use((err,
|
|
65
|
+
app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
|
|
66
66
|
console.error(err.stack);
|
|
67
67
|
res.status(500).send({
|
|
68
68
|
error: "something went wrong",
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
|
-
import
|
|
3
|
-
import * as debug from "debug";
|
|
2
|
+
import debug from "debug";
|
|
4
3
|
import type { NextFunction, Request, Response } from "express";
|
|
5
|
-
|
|
6
|
-
import Express from "express-serve-static-core";
|
|
7
|
-
import * as jwt from "jsonwebtoken";
|
|
4
|
+
import jwt from "jsonwebtoken";
|
|
8
5
|
import { JwksClient, SigningKeyNotFoundError } from "jwks-rsa";
|
|
9
6
|
|
|
10
7
|
/**
|
|
@@ -116,7 +113,7 @@ export function createJwtMiddleware(
|
|
|
116
113
|
userId: payload.userId,
|
|
117
114
|
};
|
|
118
115
|
|
|
119
|
-
next();
|
|
116
|
+
return next();
|
|
120
117
|
} catch (e) {
|
|
121
118
|
if (e instanceof JWTAuthorizationError) {
|
|
122
119
|
return sendUnauthorizedResponse(res, e.message);
|
|
@@ -125,9 +122,7 @@ export function createJwtMiddleware(
|
|
|
125
122
|
if (e instanceof SigningKeyNotFoundError) {
|
|
126
123
|
return sendUnauthorizedResponse(
|
|
127
124
|
res,
|
|
128
|
-
`Public key not found.
|
|
129
|
-
"Ensure you have the correct App_ID set",
|
|
130
|
-
)}.`,
|
|
125
|
+
`Public key not found. Ensure you have the correct App_ID set`,
|
|
131
126
|
);
|
|
132
127
|
}
|
|
133
128
|
|
|
@@ -139,7 +134,7 @@ export function createJwtMiddleware(
|
|
|
139
134
|
return sendUnauthorizedResponse(res, "Token expired");
|
|
140
135
|
}
|
|
141
136
|
|
|
142
|
-
next(e);
|
|
137
|
+
return next(e);
|
|
143
138
|
}
|
|
144
139
|
};
|
|
145
140
|
}
|
|
@@ -19,9 +19,10 @@ export class TableWrapper {
|
|
|
19
19
|
}[],
|
|
20
20
|
) {
|
|
21
21
|
this.validateRowColumn();
|
|
22
|
-
this.metaCells =
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
this.metaCells = [];
|
|
23
|
+
for (const row of this.rows) {
|
|
24
|
+
this.metaCells.push(Array.from({ length: row.cells.length }, () => ({})));
|
|
25
|
+
}
|
|
25
26
|
this.syncMergedCellsFromRows();
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -94,16 +95,14 @@ export class TableWrapper {
|
|
|
94
95
|
|
|
95
96
|
this.validateRowColumn(1, 0);
|
|
96
97
|
|
|
98
|
+
const columnLength = this.rows[0]?.cells.length || 0;
|
|
97
99
|
const newRow = {
|
|
98
|
-
cells: Array.from(
|
|
99
|
-
{ length: this.rows[0].cells.length },
|
|
100
|
-
() => ({}) as Cell,
|
|
101
|
-
),
|
|
100
|
+
cells: Array.from({ length: columnLength }, () => ({}) as Cell),
|
|
102
101
|
};
|
|
103
102
|
this.rows.splice(afterRowPos, 0, newRow);
|
|
104
103
|
|
|
105
104
|
const newMergeCells: MetaCell[] = Array.from(
|
|
106
|
-
{ length:
|
|
105
|
+
{ length: columnLength },
|
|
107
106
|
() => ({}),
|
|
108
107
|
);
|
|
109
108
|
this.metaCells.splice(afterRowPos, 0, newMergeCells);
|
|
@@ -134,22 +133,24 @@ export class TableWrapper {
|
|
|
134
133
|
* column is inserted between them, the new column will also have the same background color.
|
|
135
134
|
*/
|
|
136
135
|
addColumn(afterColumnPos: number) {
|
|
137
|
-
|
|
136
|
+
const columnLength = this.rows[0]?.cells.length || 0;
|
|
137
|
+
if (afterColumnPos < 0 || afterColumnPos > columnLength) {
|
|
138
138
|
throw new TableValidationError(
|
|
139
|
-
`New column position must be between 0 and ${
|
|
139
|
+
`New column position must be between 0 and ${columnLength}.`,
|
|
140
140
|
);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
this.validateRowColumn(0, 1);
|
|
144
144
|
|
|
145
145
|
this.rows.forEach((row) => row.cells.splice(afterColumnPos, 0, null));
|
|
146
|
+
const newColumnLength = this.rows[0]?.cells.length || 0;
|
|
146
147
|
|
|
147
148
|
const newMergeCell: MetaCell = {};
|
|
148
149
|
this.metaCells.forEach((row) =>
|
|
149
150
|
row.splice(afterColumnPos, 0, newMergeCell),
|
|
150
151
|
);
|
|
151
152
|
|
|
152
|
-
if (0 < afterColumnPos && afterColumnPos <
|
|
153
|
+
if (0 < afterColumnPos && afterColumnPos < newColumnLength) {
|
|
153
154
|
// Insert in between columns
|
|
154
155
|
for (let i = 0; i < this.rows.length; i++) {
|
|
155
156
|
this.mayCopyStyles({
|
|
@@ -178,7 +179,7 @@ export class TableWrapper {
|
|
|
178
179
|
this.validateCellBoundaries(rowPos, columnPos);
|
|
179
180
|
const rowIndex = rowPos - 1;
|
|
180
181
|
const columnIndex = columnPos - 1;
|
|
181
|
-
const { mergedInto } = this.
|
|
182
|
+
const { mergedInto } = this.getMetaCell(rowIndex, columnIndex);
|
|
182
183
|
if (!mergedInto) {
|
|
183
184
|
// Not belongs to any merged cell
|
|
184
185
|
return false;
|
|
@@ -199,7 +200,15 @@ export class TableWrapper {
|
|
|
199
200
|
`The cell at ${rowPos},${columnPos} is squashed into another cell`,
|
|
200
201
|
);
|
|
201
202
|
}
|
|
202
|
-
return this.
|
|
203
|
+
return this.getCellInternal(rowPos - 1, columnPos - 1);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
getCellInternal(rowIdx: number, columnIdx: number) {
|
|
207
|
+
const row = this.rows[rowIdx];
|
|
208
|
+
if (!row) {
|
|
209
|
+
throw new Error("validateCellBoundaries should be called first");
|
|
210
|
+
}
|
|
211
|
+
return row.cells[columnIdx];
|
|
203
212
|
}
|
|
204
213
|
|
|
205
214
|
/**
|
|
@@ -222,8 +231,12 @@ export class TableWrapper {
|
|
|
222
231
|
const rowIndex = rowPos - 1;
|
|
223
232
|
const columnIndex = columnPos - 1;
|
|
224
233
|
const { rowSpan: oldRowSpan, colSpan: oldColSpan } =
|
|
225
|
-
this.
|
|
226
|
-
|
|
234
|
+
this.getCellInternal(rowIndex, columnIndex) || {};
|
|
235
|
+
|
|
236
|
+
const row = this.rows[rowIndex];
|
|
237
|
+
if (row) {
|
|
238
|
+
row.cells[columnIndex] = details;
|
|
239
|
+
}
|
|
227
240
|
|
|
228
241
|
if (oldRowSpan !== rowSpan || oldColSpan !== colSpan) {
|
|
229
242
|
this.syncMergedCellsFromRows();
|
|
@@ -232,10 +245,11 @@ export class TableWrapper {
|
|
|
232
245
|
|
|
233
246
|
private validateRowColumn(toBeAddedRow = 0, toBeAddedColumn = 0) {
|
|
234
247
|
const rowCount = this.rows.length + toBeAddedRow;
|
|
235
|
-
const
|
|
236
|
-
if (rowCount === 0) {
|
|
248
|
+
const row = this.rows[0];
|
|
249
|
+
if (rowCount === 0 || !row) {
|
|
237
250
|
throw new TableValidationError("Table must have at least one row.");
|
|
238
251
|
}
|
|
252
|
+
const columnCount = row.cells.length + toBeAddedColumn;
|
|
239
253
|
if (columnCount === 0) {
|
|
240
254
|
throw new TableValidationError("Table must have at least one column.");
|
|
241
255
|
}
|
|
@@ -267,6 +281,9 @@ export class TableWrapper {
|
|
|
267
281
|
// Then loop through this.rows to find any merged cells
|
|
268
282
|
for (let rowIndex = 0; rowIndex < this.rows.length; rowIndex++) {
|
|
269
283
|
const row = this.rows[rowIndex];
|
|
284
|
+
if (!row) {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
270
287
|
for (let columnIndex = 0; columnIndex < row.cells.length; columnIndex++) {
|
|
271
288
|
const cell = row.cells[columnIndex] || { colSpan: 1, rowSpan: 1 };
|
|
272
289
|
const colSpan = cell.colSpan || 1;
|
|
@@ -304,7 +321,7 @@ export class TableWrapper {
|
|
|
304
321
|
) {
|
|
305
322
|
for (let row = fromRow; row <= toRow; row++) {
|
|
306
323
|
for (let column = fromColumn; column <= toColumn; column++) {
|
|
307
|
-
const metaCell = this.
|
|
324
|
+
const metaCell = this.getMetaCell(row, column);
|
|
308
325
|
|
|
309
326
|
if (metaCell.mergedInto) {
|
|
310
327
|
// This cell may be squashed by another merge cell
|
|
@@ -344,39 +361,57 @@ export class TableWrapper {
|
|
|
344
361
|
currentColumnIdx: number;
|
|
345
362
|
}) {
|
|
346
363
|
// Continue span if both front and behind cells belong to a same merged cell
|
|
347
|
-
const frontMergedCell =
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
364
|
+
const frontMergedCell = this.getMetaCell(
|
|
365
|
+
frontRowIdx,
|
|
366
|
+
frontColumnIdx,
|
|
367
|
+
).mergedInto;
|
|
368
|
+
const behindMergedCell = this.getMetaCell(
|
|
369
|
+
behindRowIdx,
|
|
370
|
+
behindColumnIdx,
|
|
371
|
+
).mergedInto;
|
|
351
372
|
if (
|
|
352
373
|
frontMergedCell &&
|
|
353
374
|
frontMergedCell.row === behindMergedCell?.row &&
|
|
354
375
|
frontMergedCell.column === behindMergedCell?.column
|
|
355
376
|
) {
|
|
356
|
-
this.
|
|
377
|
+
this.getMetaCell(currentRowIdx, currentColumnIdx).mergedInto = {
|
|
357
378
|
...frontMergedCell,
|
|
358
379
|
};
|
|
359
380
|
}
|
|
360
381
|
|
|
361
382
|
// Copy attributes if both front and behind cells are the same
|
|
362
|
-
const frontCell = this.
|
|
363
|
-
const behindCell = this.
|
|
383
|
+
const frontCell = this.getCellInternal(frontRowIdx, frontColumnIdx);
|
|
384
|
+
const behindCell = this.getCellInternal(behindRowIdx, behindColumnIdx);
|
|
364
385
|
if (
|
|
365
386
|
frontCell != null &&
|
|
366
387
|
behindCell != null &&
|
|
367
388
|
frontCell.attributes &&
|
|
368
389
|
behindCell.attributes
|
|
369
390
|
) {
|
|
370
|
-
let currentCell = this.
|
|
371
|
-
|
|
391
|
+
let currentCell = this.getCellInternal(currentRowIdx, currentColumnIdx);
|
|
392
|
+
const attrs = Object.keys(
|
|
393
|
+
frontCell.attributes,
|
|
394
|
+
) as (keyof Cell["attributes"])[];
|
|
395
|
+
for (const key of attrs) {
|
|
372
396
|
if (frontCell.attributes[key] === behindCell.attributes[key]) {
|
|
373
397
|
currentCell = currentCell || { type: "empty" };
|
|
374
398
|
currentCell.attributes = currentCell.attributes || {};
|
|
375
399
|
currentCell.attributes[key] = frontCell.attributes[key];
|
|
376
400
|
}
|
|
377
401
|
}
|
|
378
|
-
this.rows[currentRowIdx]
|
|
402
|
+
const targetRow = this.rows[currentRowIdx];
|
|
403
|
+
if (currentCell && targetRow) {
|
|
404
|
+
targetRow.cells[currentColumnIdx] = currentCell;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
private getMetaCell(rowIdx: number, columnIdx: number): MetaCell {
|
|
410
|
+
const metaCell = this.metaCells[rowIdx]?.[columnIdx];
|
|
411
|
+
if (!metaCell) {
|
|
412
|
+
throw new Error("MetaCells does not match the table dimension");
|
|
379
413
|
}
|
|
414
|
+
return metaCell;
|
|
380
415
|
}
|
|
381
416
|
|
|
382
417
|
/**
|
|
@@ -386,13 +421,17 @@ export class TableWrapper {
|
|
|
386
421
|
private syncCellSpansFromMetaCells() {
|
|
387
422
|
const groups = new Map<string, { row: number; column: number }[]>();
|
|
388
423
|
for (let row = 0; row < this.metaCells.length; row++) {
|
|
389
|
-
|
|
424
|
+
const metaRow = this.metaCells[row];
|
|
425
|
+
if (!metaRow) {
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
for (let column = 0; column < metaRow.length; column++) {
|
|
390
429
|
// Reset all rowSpans and colSpans
|
|
391
|
-
const currentCell = this.
|
|
430
|
+
const currentCell = this.getCellInternal(row, column);
|
|
392
431
|
currentCell && delete currentCell.rowSpan;
|
|
393
432
|
currentCell && delete currentCell.colSpan;
|
|
394
433
|
|
|
395
|
-
const mergedCell = this.
|
|
434
|
+
const mergedCell = this.getMetaCell(row, column);
|
|
396
435
|
if (!mergedCell.mergedInto) {
|
|
397
436
|
continue;
|
|
398
437
|
}
|
|
@@ -429,12 +468,15 @@ export class TableWrapper {
|
|
|
429
468
|
const rowSpan = maxRow - minRow + 1;
|
|
430
469
|
const columnSpan = maxColumn - minColumn + 1;
|
|
431
470
|
if (rowSpan > 1 || columnSpan > 1) {
|
|
432
|
-
const currentCell = this.
|
|
471
|
+
const currentCell = this.getCellInternal(minRow, minColumn) || {
|
|
433
472
|
type: "empty",
|
|
434
473
|
};
|
|
435
474
|
currentCell.rowSpan = rowSpan;
|
|
436
475
|
currentCell.colSpan = columnSpan;
|
|
437
|
-
this.rows[minRow]
|
|
476
|
+
const targetRow = this.rows[minRow];
|
|
477
|
+
if (targetRow) {
|
|
478
|
+
targetRow.cells[minColumn] = currentCell;
|
|
479
|
+
}
|
|
438
480
|
}
|
|
439
481
|
});
|
|
440
482
|
}
|
|
@@ -450,9 +492,10 @@ export class TableWrapper {
|
|
|
450
492
|
`Row position must be between 1 and ${this.rows.length} (number of rows).`,
|
|
451
493
|
);
|
|
452
494
|
}
|
|
453
|
-
|
|
495
|
+
const columnLength = this.rows[0]?.cells.length || 0;
|
|
496
|
+
if (columnPos < 1 || columnPos > columnLength) {
|
|
454
497
|
throw new TableValidationError(
|
|
455
|
-
`Column position must be between 1 and ${
|
|
498
|
+
`Column position must be between 1 and ${columnLength} (number of columns).`,
|
|
456
499
|
);
|
|
457
500
|
}
|
|
458
501
|
if (rowSpan < 1) {
|
|
@@ -466,9 +509,9 @@ export class TableWrapper {
|
|
|
466
509
|
`Cannot expand ${rowSpan} rows from the cell at ${rowPos},${columnPos}. Table has ${this.rows.length} rows.`,
|
|
467
510
|
);
|
|
468
511
|
}
|
|
469
|
-
if (columnPos + columnSpan - 1 >
|
|
512
|
+
if (columnPos + columnSpan - 1 > columnLength) {
|
|
470
513
|
throw new TableValidationError(
|
|
471
|
-
`Cannot expand ${columnSpan} columns from the cell at ${rowPos},${columnPos}. Table has ${
|
|
514
|
+
`Cannot expand ${columnSpan} columns from the cell at ${rowPos},${columnPos}. Table has ${columnLength} columns.`,
|
|
472
515
|
);
|
|
473
516
|
}
|
|
474
517
|
}
|
|
@@ -30,6 +30,11 @@ export const useAddElement = () => {
|
|
|
30
30
|
} else if (features.isSupported(addElementAtCursor)) {
|
|
31
31
|
return addElementAtCursor(element);
|
|
32
32
|
}
|
|
33
|
+
// eslint-disable-next-line no-console
|
|
34
|
+
console.warn(
|
|
35
|
+
"Neither addElementAtPoint nor addElementAtCursor are supported",
|
|
36
|
+
);
|
|
37
|
+
return Promise.resolve();
|
|
33
38
|
};
|
|
34
39
|
});
|
|
35
40
|
|
|
@@ -40,6 +45,11 @@ export const useAddElement = () => {
|
|
|
40
45
|
} else if (isSupported(addElementAtCursor)) {
|
|
41
46
|
return addElementAtCursor(element);
|
|
42
47
|
}
|
|
48
|
+
// eslint-disable-next-line no-console
|
|
49
|
+
console.warn(
|
|
50
|
+
"Neither addElementAtPoint nor addElementAtCursor are supported",
|
|
51
|
+
);
|
|
52
|
+
return Promise.resolve();
|
|
43
53
|
};
|
|
44
54
|
setAddElement(() => addElement);
|
|
45
55
|
}, [isSupported]);
|
|
@@ -57,11 +57,13 @@ export function useOverlay<
|
|
|
57
57
|
const open = async (
|
|
58
58
|
opts: { launchParameters?: unknown } = {},
|
|
59
59
|
): Promise<AppProcessId | undefined> => {
|
|
60
|
-
if (overlay
|
|
61
|
-
|
|
62
|
-
setOverlayId(overlayId);
|
|
63
|
-
return overlayId;
|
|
60
|
+
if (!overlay || !overlay.canOpen) {
|
|
61
|
+
return undefined;
|
|
64
62
|
}
|
|
63
|
+
|
|
64
|
+
const overlayId = await overlay.open(opts);
|
|
65
|
+
setOverlayId(overlayId);
|
|
66
|
+
return overlayId;
|
|
65
67
|
};
|
|
66
68
|
|
|
67
69
|
const close = async (opts: C) => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Container, Resource } from "@canva/app-components";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import express from "express";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Generates a unique hash for a
|
|
7
|
-
*
|
|
6
|
+
* Generates a unique hash for a URL.
|
|
7
|
+
* Used for creating unique identifiers for digital assets from external URLs.
|
|
8
8
|
*/
|
|
9
9
|
export async function generateHash(message: string) {
|
|
10
10
|
const msgUint8 = new TextEncoder().encode(message);
|
|
@@ -16,6 +16,7 @@ export async function generateHash(message: string) {
|
|
|
16
16
|
return hashHex;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
// Mock image URLs for demonstration purposes - replace with your actual digital asset sources
|
|
19
20
|
const imageUrls = [
|
|
20
21
|
"https://images.pexels.com/photos/1495580/pexels-photo-1495580.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
|
|
21
22
|
"https://images.pexels.com/photos/3943197/pexels-photo-3943197.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
|
|
@@ -23,15 +24,20 @@ const imageUrls = [
|
|
|
23
24
|
"https://images.pexels.com/photos/2904142/pexels-photo-2904142.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
|
|
24
25
|
"https://images.pexels.com/photos/5403478/pexels-photo-5403478.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
|
|
25
26
|
];
|
|
27
|
+
|
|
26
28
|
export const createDamRouter = () => {
|
|
27
29
|
const router = express.Router();
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
/*
|
|
32
|
+
Main endpoint for finding digital assets and containers.
|
|
33
|
+
This should be replaced with actual integration to your digital asset management system.
|
|
34
|
+
*/
|
|
32
35
|
router.post("/resources/find", async (req, res) => {
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
/*
|
|
37
|
+
Extract relevant fields from the FindResourcesRequest.
|
|
38
|
+
Replace this mock implementation with actual queries to your digital asset management system.
|
|
39
|
+
Consider implementing proper filtering, sorting, and pagination based on these parameters.
|
|
40
|
+
*/
|
|
35
41
|
const {
|
|
36
42
|
types,
|
|
37
43
|
continuation,
|
|
@@ -48,21 +54,32 @@ export const createDamRouter = () => {
|
|
|
48
54
|
} = req.body;
|
|
49
55
|
|
|
50
56
|
let resources: Resource[] = [];
|
|
57
|
+
|
|
58
|
+
// Handle image resource requests
|
|
51
59
|
if (types.includes("IMAGE")) {
|
|
52
60
|
resources = await Promise.all(
|
|
53
|
-
Array.from({ length: 40 }, async (_, i) =>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
Array.from({ length: 40 }, async (_, i) => {
|
|
62
|
+
const imageUrl = imageUrls[i % imageUrls.length];
|
|
63
|
+
|
|
64
|
+
if (!imageUrl) {
|
|
65
|
+
throw new Error(`Image URL not found for index ${i}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
id: await generateHash(i + ""),
|
|
70
|
+
mimeType: "image/jpeg",
|
|
71
|
+
name: `My new thing in ${locale}`, // Uses locale for demonstration - implement proper i18n
|
|
72
|
+
type: "IMAGE",
|
|
73
|
+
thumbnail: {
|
|
74
|
+
url: imageUrl,
|
|
75
|
+
},
|
|
76
|
+
url: imageUrl,
|
|
77
|
+
};
|
|
78
|
+
}),
|
|
63
79
|
);
|
|
64
80
|
}
|
|
65
81
|
|
|
82
|
+
// Handle container (folder) resource requests
|
|
66
83
|
if (types.includes("CONTAINER")) {
|
|
67
84
|
const containers = await Promise.all(
|
|
68
85
|
Array.from(
|
|
@@ -80,6 +97,7 @@ export const createDamRouter = () => {
|
|
|
80
97
|
resources = resources.concat(containers);
|
|
81
98
|
}
|
|
82
99
|
|
|
100
|
+
// Send response with resources and pagination token
|
|
83
101
|
res.send({
|
|
84
102
|
resources,
|
|
85
103
|
continuation: +(continuation || 0) + 1,
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@canva/app-components": "^2.1.0",
|
|
22
22
|
"@canva/app-i18n-kit": "^1.1.1",
|
|
23
|
-
"@canva/app-ui-kit": "^5.2.
|
|
23
|
+
"@canva/app-ui-kit": "^5.2.1",
|
|
24
24
|
"@canva/asset": "^2.2.1",
|
|
25
25
|
"@canva/design": "^2.7.3",
|
|
26
26
|
"@canva/error": "^2.1.0",
|
|
@@ -44,7 +44,6 @@
|
|
|
44
44
|
"@types/cors": "2.8.19",
|
|
45
45
|
"@types/debug": "4.1.12",
|
|
46
46
|
"@types/express": "4.17.21",
|
|
47
|
-
"@types/express-serve-static-core": "5.0.7",
|
|
48
47
|
"@types/jest": "29.5.14",
|
|
49
48
|
"@types/jsonwebtoken": "9.0.10",
|
|
50
49
|
"@types/node": "20.19.2",
|