@canva/cli 1.15.0 → 1.17.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 (27) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/cli.js +360 -360
  3. package/package.json +1 -1
  4. package/templates/base/backend/base_backend/create.ts +2 -2
  5. package/templates/base/styles/components.css +0 -18
  6. package/templates/common/jest.setup.ts +3 -1
  7. package/templates/content_publisher/package.json +1 -1
  8. package/templates/content_publisher/src/intents/content_publisher/index.tsx +2 -2
  9. package/templates/content_publisher/src/intents/content_publisher/post_preview.tsx +34 -20
  10. package/templates/content_publisher/src/intents/content_publisher/preview_ui.tsx +11 -2
  11. package/templates/content_publisher/src/intents/content_publisher/settings_ui.tsx +23 -13
  12. package/templates/content_publisher/styles/components.css +0 -18
  13. package/templates/content_publisher/styles/preview_ui.css +0 -39
  14. package/templates/dam/package.json +2 -2
  15. package/templates/dam/src/intents/design_editor/app.tsx +1 -1
  16. package/templates/dam/src/intents/design_editor/index.css +0 -7
  17. package/templates/dam/utils/backend/base_backend/create.ts +2 -2
  18. package/templates/data_connector/package.json +1 -1
  19. package/templates/data_connector/styles/components.css +0 -18
  20. package/templates/gen_ai/package.json +2 -2
  21. package/templates/gen_ai/styles/components.css +0 -18
  22. package/templates/gen_ai/utils/backend/base_backend/create.ts +2 -2
  23. package/templates/hello_world/package.json +1 -1
  24. package/templates/hello_world/styles/components.css +0 -18
  25. package/templates/mls/jest.setup.ts +3 -1
  26. package/templates/mls/package.json +1 -1
  27. package/templates/mls/styles/components.css +0 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canva/cli",
3
- "version": "1.15.0",
3
+ "version": "1.17.0",
4
4
  "description": "The official Canva CLI.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "author": "Canva Pty Ltd.",
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable no-console */
2
- import { UserAuthError } from "@canva/app-middleware";
2
+ import { TokenVerificationError } from "@canva/app-middleware";
3
3
  import debug from "debug";
4
4
  import express from "express";
5
5
  import type { NextFunction, Request, Response } from "express";
@@ -65,7 +65,7 @@ export function createBaseServer(router: express.Router): BaseServer {
65
65
  // default error handler
66
66
  app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
67
67
  // Handle authentication errors from @canva/app-middleware
68
- if (err instanceof UserAuthError) {
68
+ if (err instanceof TokenVerificationError) {
69
69
  res.status(err.statusCode).json({
70
70
  error: err.code,
71
71
  message: err.message,
@@ -36,21 +36,3 @@
36
36
  .scrollContainer:focus-within::-webkit-scrollbar-thumb {
37
37
  visibility: visible;
38
38
  }
39
-
40
- /* Main container for the content publisher preview UI */
41
- .previewContainer {
42
- display: flex;
43
- align-items: center;
44
- justify-content: center;
45
- flex-direction: column;
46
- width: 100%;
47
- height: 100%;
48
- }
49
-
50
- /* Wrapper for the content publisher post preview */
51
- .previewWrapper {
52
- display: flex;
53
- align-items: center;
54
- justify-content: center;
55
- width: calc(400px + 32px + 2px); /* Image width + padding + border */
56
- }
@@ -4,6 +4,7 @@
4
4
  import * as asset from "@canva/asset/test";
5
5
  import * as design from "@canva/design/test";
6
6
  import * as error from "@canva/error/test";
7
+ import * as intents from "@canva/intents/test";
7
8
  import * as platform from "@canva/platform/test";
8
9
  import * as user from "@canva/user/test";
9
10
 
@@ -14,6 +15,7 @@ import * as user from "@canva/user/test";
14
15
  asset.initTestEnvironment();
15
16
  design.initTestEnvironment();
16
17
  error.initTestEnvironment();
18
+ intents.initTestEnvironment();
17
19
  platform.initTestEnvironment();
18
20
  user.initTestEnvironment();
19
21
 
@@ -23,9 +25,9 @@ user.initTestEnvironment();
23
25
  */
24
26
  jest.mock("@canva/asset");
25
27
  jest.mock("@canva/design");
28
+ jest.mock("@canva/intents");
26
29
  jest.mock("@canva/platform");
27
30
  jest.mock("@canva/user");
28
-
29
31
  /*
30
32
  Important: @canva/error should not be mocked
31
33
  Use it to simulate API error responses from other mocks by throwing CanvaError instances
@@ -25,7 +25,7 @@
25
25
  "@canva/asset": "^2.3.0",
26
26
  "@canva/design": "^2.7.5",
27
27
  "@canva/error": "^2.2.0",
28
- "@canva/intents": "^2.0.2-beta.3",
28
+ "@canva/intents": "^2.1.0",
29
29
  "@canva/platform": "^2.2.1",
30
30
  "@canva/user": "^2.1.2",
31
31
  "react": "^19.2.3",
@@ -18,7 +18,7 @@ const intl = initIntl();
18
18
  // Render the settings UI where users configure publishing options
19
19
  function renderSettingsUi({
20
20
  updatePublishSettings,
21
- registerOnSettingsUiContextChange,
21
+ registerOnContextChange,
22
22
  }: RenderSettingsUiRequest) {
23
23
  const root = createRoot(document.getElementById("root") as Element);
24
24
  root.render(
@@ -26,7 +26,7 @@ function renderSettingsUi({
26
26
  <AppUiProvider>
27
27
  <SettingsUi
28
28
  updatePublishSettings={updatePublishSettings}
29
- registerOnSettingsUiContextChange={registerOnSettingsUiContextChange}
29
+ registerOnContextChange={registerOnContextChange}
30
30
  />
31
31
  </AppUiProvider>
32
32
  </AppI18nProvider>,
@@ -32,20 +32,28 @@ export const PostPreview = ({
32
32
 
33
33
  const caption = settings?.caption;
34
34
 
35
+ // TODO: You should update this value to match the visuals you're hoping to achieve with your export preview
36
+ // Image width + padding + border
37
+ const previewWidth = 400 + 32 + 2;
38
+
35
39
  return (
36
- <Box
37
- className={styles.wrapper}
38
- background="surface"
39
- borderRadius="large"
40
- padding="2u"
41
- border="standard"
42
- >
43
- <Rows spacing="2u">
44
- <UserInfo isLoading={isLoading} username={username} />
45
- <ImagePreview previewMedia={previewMedia} />
46
- <Caption isLoading={isLoading} caption={caption} />
47
- </Rows>
48
- </Box>
40
+ <div style={{ width: previewWidth }}>
41
+ <Box
42
+ display="flex"
43
+ alignItems="center"
44
+ justifyContent="center"
45
+ background="surface"
46
+ borderRadius="large"
47
+ padding="2u"
48
+ border="standard"
49
+ >
50
+ <Rows spacing="2u">
51
+ <UserInfo isLoading={isLoading} username={username} />
52
+ <ImagePreview previewMedia={previewMedia} />
53
+ <Caption isLoading={isLoading} caption={caption} />
54
+ </Rows>
55
+ </Box>
56
+ </div>
49
57
  );
50
58
  };
51
59
 
@@ -118,13 +126,15 @@ const ImagePreview = ({
118
126
  </div>
119
127
  ) : (
120
128
  <div className={styles.imageRow} style={{ width: fullWidth }}>
121
- {media?.previews.map((p) => {
122
- return (
123
- <div key={p.id} className={styles.image}>
124
- <PreviewRenderer preview={p} />
125
- </div>
126
- );
127
- })}
129
+ {media?.previews
130
+ .filter((p) => p.kind !== "email")
131
+ .map((p) => {
132
+ return (
133
+ <div key={p.id} className={styles.image}>
134
+ <PreviewRenderer preview={p} />
135
+ </div>
136
+ );
137
+ })}
128
138
  </div>
129
139
  )}
130
140
  </Box>
@@ -133,6 +143,10 @@ const ImagePreview = ({
133
143
 
134
144
  // Renders individual preview based on its type and status
135
145
  const PreviewRenderer = ({ preview }: { preview: Preview }) => {
146
+ if (preview.kind === "email") {
147
+ return null;
148
+ }
149
+
136
150
  const intl = useIntl();
137
151
  // Handle different preview states
138
152
  if (preview.status === "loading") {
@@ -1,3 +1,4 @@
1
+ import { Box } from "@canva/app-ui-kit";
1
2
  import type { OutputType, PreviewMedia } from "@canva/intents/content";
2
3
  import { useEffect, useState } from "react";
3
4
  import * as styles from "../../../styles/preview_ui.css";
@@ -40,7 +41,15 @@ export const PreviewUi = ({ registerOnPreviewChange }: PreviewUiProps) => {
40
41
  const publishSettings = parsePublishSettings(publishRef);
41
42
 
42
43
  return (
43
- <div className={styles.container}>
44
+ <Box
45
+ className={styles.container}
46
+ display="flex"
47
+ alignItems="center"
48
+ justifyContent="center"
49
+ flexDirection="column"
50
+ width="full"
51
+ height="full"
52
+ >
44
53
  {outputType?.id === "post" && (
45
54
  <PostPreview
46
55
  previewMedia={previewMedia}
@@ -48,6 +57,6 @@ export const PreviewUi = ({ registerOnPreviewChange }: PreviewUiProps) => {
48
57
  username={username}
49
58
  />
50
59
  )}
51
- </div>
60
+ </Box>
52
61
  );
53
62
  };
@@ -1,10 +1,10 @@
1
1
  import { FormField, Rows, Text, TextInput } from "@canva/app-ui-kit";
2
2
  import type {
3
3
  PublishRefValidityState,
4
+ PublishSettingsSettingsUiContext,
4
5
  RenderSettingsUiRequest,
5
- SettingsUiContext,
6
6
  } from "@canva/intents/content";
7
- import { useEffect, useState } from "react";
7
+ import { useCallback, useEffect, useState } from "react";
8
8
  import { useIntl } from "react-intl";
9
9
  import * as styles from "styles/components.css";
10
10
  import type { PublishSettings } from "./types";
@@ -12,20 +12,35 @@ import type { PublishSettings } from "./types";
12
12
  // Settings UI component for configuring publish settings
13
13
  export const SettingsUi = ({
14
14
  updatePublishSettings,
15
- registerOnSettingsUiContextChange,
15
+ registerOnContextChange,
16
16
  }: RenderSettingsUiRequest) => {
17
17
  const intl = useIntl();
18
18
  const [settings, setSettings] = useState<PublishSettings>({ caption: "" });
19
19
  const [settingsUiContext, setSettingsUiContext] =
20
- useState<SettingsUiContext | null>(null);
20
+ useState<PublishSettingsSettingsUiContext | null>(null);
21
21
 
22
22
  // Listen for settings UI context changes (e.g., when output type changes)
23
23
  useEffect(() => {
24
- const dispose = registerOnSettingsUiContextChange((context) => {
25
- setSettingsUiContext(context);
24
+ const dispose = registerOnContextChange({
25
+ onContextChange: (context) => {
26
+ if (context.reason !== "publish_settings") return;
27
+ setSettingsUiContext(context);
28
+ },
26
29
  });
27
30
  return dispose;
28
- }, [registerOnSettingsUiContextChange]);
31
+ }, [registerOnContextChange]);
32
+
33
+ // Helper function to both set the settings locally and propagate them to Canva
34
+ const setAndPropagateSettings = useCallback(
35
+ (updatedSettings: PublishSettings) => {
36
+ setSettings(updatedSettings);
37
+ updatePublishSettings({
38
+ publishRef: JSON.stringify(updatedSettings),
39
+ validityState: validatePublishRef(updatedSettings),
40
+ });
41
+ },
42
+ [updatePublishSettings],
43
+ );
29
44
 
30
45
  return (
31
46
  <div className={styles.scrollContainer}>
@@ -43,12 +58,7 @@ export const SettingsUi = ({
43
58
  {...props}
44
59
  value={settings.caption}
45
60
  onChange={(caption) => {
46
- const updatedSettings = { ...settings, caption };
47
- setSettings(updatedSettings);
48
- updatePublishSettings({
49
- publishRef: JSON.stringify(settings),
50
- validityState: validatePublishRef(settings),
51
- });
61
+ setAndPropagateSettings({ ...settings, caption });
52
62
  }}
53
63
  />
54
64
  )}
@@ -36,21 +36,3 @@
36
36
  .scrollContainer:focus-within::-webkit-scrollbar-thumb {
37
37
  visibility: visible;
38
38
  }
39
-
40
- /* Main container for the content publisher preview UI */
41
- .previewContainer {
42
- display: flex;
43
- align-items: center;
44
- justify-content: center;
45
- flex-direction: column;
46
- width: 100%;
47
- height: 100%;
48
- }
49
-
50
- /* Wrapper for the content publisher post preview */
51
- .previewWrapper {
52
- display: flex;
53
- align-items: center;
54
- justify-content: center;
55
- width: calc(400px + 32px + 2px); /* Image width + padding + border */
56
- }
@@ -1,13 +1,3 @@
1
- /* Main container for the preview UI */
2
- .container {
3
- display: flex;
4
- align-items: center;
5
- justify-content: center;
6
- flex-direction: column;
7
- width: 100%;
8
- height: 100%;
9
- }
10
-
11
1
  /* Scale down preview on mobile devices */
12
2
  @media (max-width: 600px) {
13
3
  .container {
@@ -15,14 +5,6 @@
15
5
  }
16
6
  }
17
7
 
18
- /* Wrapper for the social media post preview */
19
- .wrapper {
20
- display: flex;
21
- align-items: center;
22
- justify-content: center;
23
- width: calc(400px + 32px + 2px); /* Image width + padding + border */
24
- }
25
-
26
8
  /* User profile section styling */
27
9
  .user {
28
10
  display: flex;
@@ -38,12 +20,6 @@
38
20
  transform-origin: top left;
39
21
  }
40
22
 
41
- .avatarImage {
42
- width: 100%;
43
- height: 100%;
44
- object-fit: cover;
45
- }
46
-
47
23
  /* Text placeholder for loading states */
48
24
  .textPlaceholder {
49
25
  min-width: calc(8 * 20);
@@ -62,15 +38,6 @@
62
38
  display: flex;
63
39
  }
64
40
 
65
- /* Aspect ratio helpers for different image formats */
66
- .aspect-1-1 {
67
- padding-top: 100%;
68
- }
69
-
70
- .aspect-3-4 {
71
- padding-top: 133.33%;
72
- }
73
-
74
41
  /* Individual image and placeholder styling */
75
42
  .imagePlaceholder,
76
43
  .image {
@@ -80,9 +47,3 @@
80
47
  display: inline-block;
81
48
  position: relative;
82
49
  }
83
-
84
- /* Icon placeholder styling */
85
- .iconPlaceholder {
86
- width: 24px;
87
- height: 24px;
88
- }
@@ -21,12 +21,12 @@
21
21
  "@canva/app-components": "^2.1.0",
22
22
  "@canva/app-hooks": "^0.0.0-beta.4",
23
23
  "@canva/app-i18n-kit": "^1.2.0",
24
- "@canva/app-middleware": "^0.0.0-beta.4",
24
+ "@canva/app-middleware": "^0.0.0-beta.5",
25
25
  "@canva/app-ui-kit": "^5.5.0",
26
26
  "@canva/asset": "^2.3.0",
27
27
  "@canva/design": "^2.7.5",
28
28
  "@canva/error": "^2.2.0",
29
- "@canva/intents": "^2.0.0",
29
+ "@canva/intents": "^2.1.0",
30
30
  "@canva/platform": "^2.2.1",
31
31
  "@canva/user": "^2.1.2",
32
32
  "cookie-parser": "1.4.7",
@@ -8,7 +8,7 @@ import * as styles from "./index.css";
8
8
  export function App() {
9
9
  const config = useConfig();
10
10
  return (
11
- <Box className={styles.rootWrapper}>
11
+ <Box className={styles.rootWrapper} height="full">
12
12
  <SearchableListView
13
13
  config={config}
14
14
  findResources={findResources}
@@ -1,10 +1,3 @@
1
1
  .rootWrapper {
2
- height: 100%;
3
2
  overflow-y: hidden;
4
3
  }
5
-
6
- .centerInPage {
7
- display: flex;
8
- flex-direction: column;
9
- justify-content: center;
10
- }
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable no-console */
2
- import { UserAuthError } from "@canva/app-middleware";
2
+ import { TokenVerificationError } from "@canva/app-middleware";
3
3
  import debug from "debug";
4
4
  import express from "express";
5
5
  import type { NextFunction, Request, Response } from "express";
@@ -65,7 +65,7 @@ export function createBaseServer(router: express.Router): BaseServer {
65
65
  // default error handler
66
66
  app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
67
67
  // Handle authentication errors from @canva/app-middleware
68
- if (err instanceof UserAuthError) {
68
+ if (err instanceof TokenVerificationError) {
69
69
  res.status(err.statusCode).json({
70
70
  error: err.code,
71
71
  message: err.message,
@@ -25,7 +25,7 @@
25
25
  "@canva/asset": "^2.3.0",
26
26
  "@canva/design": "^2.7.5",
27
27
  "@canva/error": "^2.2.0",
28
- "@canva/intents": "^2.0.0",
28
+ "@canva/intents": "^2.1.0",
29
29
  "@canva/platform": "^2.2.1",
30
30
  "@canva/user": "^2.1.2",
31
31
  "react": "^19.2.3",
@@ -36,21 +36,3 @@
36
36
  .scrollContainer:focus-within::-webkit-scrollbar-thumb {
37
37
  visibility: visible;
38
38
  }
39
-
40
- /* Main container for the content publisher preview UI */
41
- .previewContainer {
42
- display: flex;
43
- align-items: center;
44
- justify-content: center;
45
- flex-direction: column;
46
- width: 100%;
47
- height: 100%;
48
- }
49
-
50
- /* Wrapper for the content publisher post preview */
51
- .previewWrapper {
52
- display: flex;
53
- align-items: center;
54
- justify-content: center;
55
- width: calc(400px + 32px + 2px); /* Image width + padding + border */
56
- }
@@ -20,12 +20,12 @@
20
20
  "dependencies": {
21
21
  "@canva/app-hooks": "^0.0.0-beta.4",
22
22
  "@canva/app-i18n-kit": "^1.2.0",
23
- "@canva/app-middleware": "^0.0.0-beta.4",
23
+ "@canva/app-middleware": "^0.0.0-beta.5",
24
24
  "@canva/app-ui-kit": "^5.5.0",
25
25
  "@canva/asset": "^2.3.0",
26
26
  "@canva/design": "^2.7.5",
27
27
  "@canva/error": "^2.2.0",
28
- "@canva/intents": "^2.0.0",
28
+ "@canva/intents": "^2.1.0",
29
29
  "@canva/platform": "^2.2.1",
30
30
  "@canva/user": "^2.1.2",
31
31
  "cookie-parser": "1.4.7",
@@ -36,21 +36,3 @@
36
36
  .scrollContainer:focus-within::-webkit-scrollbar-thumb {
37
37
  visibility: visible;
38
38
  }
39
-
40
- /* Main container for the content publisher preview UI */
41
- .previewContainer {
42
- display: flex;
43
- align-items: center;
44
- justify-content: center;
45
- flex-direction: column;
46
- width: 100%;
47
- height: 100%;
48
- }
49
-
50
- /* Wrapper for the content publisher post preview */
51
- .previewWrapper {
52
- display: flex;
53
- align-items: center;
54
- justify-content: center;
55
- width: calc(400px + 32px + 2px); /* Image width + padding + border */
56
- }
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable no-console */
2
- import { UserAuthError } from "@canva/app-middleware";
2
+ import { TokenVerificationError } from "@canva/app-middleware";
3
3
  import debug from "debug";
4
4
  import express from "express";
5
5
  import type { NextFunction, Request, Response } from "express";
@@ -65,7 +65,7 @@ export function createBaseServer(router: express.Router): BaseServer {
65
65
  // default error handler
66
66
  app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
67
67
  // Handle authentication errors from @canva/app-middleware
68
- if (err instanceof UserAuthError) {
68
+ if (err instanceof TokenVerificationError) {
69
69
  res.status(err.statusCode).json({
70
70
  error: err.code,
71
71
  message: err.message,
@@ -25,7 +25,7 @@
25
25
  "@canva/asset": "^2.3.0",
26
26
  "@canva/design": "^2.7.5",
27
27
  "@canva/error": "^2.2.0",
28
- "@canva/intents": "^2.0.0",
28
+ "@canva/intents": "^2.1.0",
29
29
  "@canva/platform": "^2.2.1",
30
30
  "@canva/user": "^2.1.2",
31
31
  "react": "^19.2.3",
@@ -36,21 +36,3 @@
36
36
  .scrollContainer:focus-within::-webkit-scrollbar-thumb {
37
37
  visibility: visible;
38
38
  }
39
-
40
- /* Main container for the content publisher preview UI */
41
- .previewContainer {
42
- display: flex;
43
- align-items: center;
44
- justify-content: center;
45
- flex-direction: column;
46
- width: 100%;
47
- height: 100%;
48
- }
49
-
50
- /* Wrapper for the content publisher post preview */
51
- .previewWrapper {
52
- display: flex;
53
- align-items: center;
54
- justify-content: center;
55
- width: calc(400px + 32px + 2px); /* Image width + padding + border */
56
- }
@@ -4,6 +4,7 @@
4
4
  import * as asset from "@canva/asset/test";
5
5
  import * as design from "@canva/design/test";
6
6
  import * as error from "@canva/error/test";
7
+ import * as intents from "@canva/intents/test";
7
8
  import * as platform from "@canva/platform/test";
8
9
  import * as user from "@canva/user/test";
9
10
  import { TextEncoder } from "util";
@@ -18,6 +19,7 @@ global.TextEncoder = TextEncoder as unknown as typeof global.TextEncoder;
18
19
  asset.initTestEnvironment();
19
20
  design.initTestEnvironment();
20
21
  error.initTestEnvironment();
22
+ intents.initTestEnvironment();
21
23
  platform.initTestEnvironment();
22
24
  user.initTestEnvironment();
23
25
 
@@ -27,9 +29,9 @@ user.initTestEnvironment();
27
29
  */
28
30
  jest.mock("@canva/asset");
29
31
  jest.mock("@canva/design");
32
+ jest.mock("@canva/intents");
30
33
  jest.mock("@canva/platform");
31
34
  jest.mock("@canva/user");
32
-
33
35
  /*
34
36
  Important: @canva/error should not be mocked
35
37
  Use it to simulate API error responses from other mocks by throwing CanvaError instances
@@ -27,7 +27,7 @@
27
27
  "@canva/asset": "^2.3.0",
28
28
  "@canva/design": "^2.7.5",
29
29
  "@canva/error": "^2.2.0",
30
- "@canva/intents": "^2.0.0",
30
+ "@canva/intents": "^2.1.0",
31
31
  "@canva/platform": "^2.2.1",
32
32
  "@canva/user": "^2.1.2",
33
33
  "@tanstack/react-query": "5.87.1",
@@ -36,21 +36,3 @@
36
36
  .scrollContainer:focus-within::-webkit-scrollbar-thumb {
37
37
  visibility: visible;
38
38
  }
39
-
40
- /* Main container for the content publisher preview UI */
41
- .previewContainer {
42
- display: flex;
43
- align-items: center;
44
- justify-content: center;
45
- flex-direction: column;
46
- width: 100%;
47
- height: 100%;
48
- }
49
-
50
- /* Wrapper for the content publisher post preview */
51
- .previewWrapper {
52
- display: flex;
53
- align-items: center;
54
- justify-content: center;
55
- width: calc(400px + 32px + 2px); /* Image width + padding + border */
56
- }