@notionhq/custom-blocks 0.1.17 → 0.1.19

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 (47) hide show
  1. package/dist/bridge/SandboxBridge.d.ts +2 -1
  2. package/dist/bridge/SandboxBridge.d.ts.map +1 -1
  3. package/dist/bridge/SandboxBridge.js +41 -5
  4. package/dist/bridge/appearance.d.ts +15 -0
  5. package/dist/bridge/appearance.d.ts.map +1 -0
  6. package/dist/bridge/appearance.js +14 -0
  7. package/dist/bridge/sandboxClient.d.ts +1 -0
  8. package/dist/bridge/sandboxClient.d.ts.map +1 -1
  9. package/dist/bridge/sandboxClient.js +26 -17
  10. package/dist/init.d.ts.map +1 -1
  11. package/dist/init.js +1 -0
  12. package/dist/protocol/dataSources/dataSource.d.ts +12 -12
  13. package/dist/protocol/dataSources/dataSourcePage.d.ts +1 -1
  14. package/dist/protocol/ids.d.ts +10 -26
  15. package/dist/protocol/ids.d.ts.map +1 -1
  16. package/dist/protocol/ids.js +25 -5
  17. package/dist/protocol/messages/createPage.d.ts +8 -8
  18. package/dist/protocol/messages/createPageResult.d.ts +5 -5
  19. package/dist/protocol/messages/currentUserChanged.d.ts +1 -1
  20. package/dist/protocol/messages/dataSourcesChanged.d.ts +3 -3
  21. package/dist/protocol/messages/getPage.d.ts +6 -6
  22. package/dist/protocol/messages/getUser.d.ts +2 -2
  23. package/dist/protocol/messages/getUser.js +2 -2
  24. package/dist/protocol/messages/hostToSandbox.d.ts +45 -45
  25. package/dist/protocol/messages/init.d.ts +14 -14
  26. package/dist/protocol/messages/listUsers.d.ts +1 -1
  27. package/dist/protocol/messages/pageChanged.d.ts +5 -5
  28. package/dist/protocol/messages/parentChanged.d.ts +4 -4
  29. package/dist/protocol/messages/queryDataSource.d.ts +1 -1
  30. package/dist/protocol/messages/queryDataSourceResult.d.ts +1 -1
  31. package/dist/protocol/messages/sandboxToHost.d.ts +8 -8
  32. package/dist/protocol/messages/updatePage.d.ts +1 -1
  33. package/dist/protocol/messages/updatePageResult.d.ts +5 -5
  34. package/dist/protocol/pages/page.d.ts +10 -10
  35. package/dist/protocol/parent.d.ts +4 -4
  36. package/dist/protocol/users/user.d.ts +4 -7
  37. package/dist/protocol/users/user.d.ts.map +1 -1
  38. package/dist/protocol/users/user.js +2 -1
  39. package/dist/react/standalonePreview.d.ts.map +1 -1
  40. package/dist/react/standalonePreview.js +3 -1
  41. package/dist/version.js +1 -1
  42. package/package.json +1 -1
  43. package/src/bridge/SandboxBridge.ts +49 -6
  44. package/src/bridge/appearance.ts +14 -0
  45. package/src/bridge/sandboxClient.ts +28 -17
  46. package/src/init.ts +1 -0
  47. package/src/react/standalonePreview.ts +3 -1
@@ -17,22 +17,22 @@ export declare const updatePageResultMessageSchema: v.VariantSchema<"status", [v
17
17
  readonly status: v.LiteralSchema<"success", undefined>;
18
18
  readonly page: v.ObjectSchema<{
19
19
  readonly object: v.LiteralSchema<"page", undefined>;
20
- readonly id: v.CustomSchema<import("../ids.js").NotionPageId, v.ErrorMessage<v.CustomIssue> | undefined>;
20
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionPageId">]>;
21
21
  readonly parent: v.VariantSchema<"type", [v.ObjectSchema<{
22
22
  readonly type: v.LiteralSchema<"page_id", undefined>;
23
- readonly page_id: v.CustomSchema<import("../ids.js").NotionPageId, v.ErrorMessage<v.CustomIssue> | undefined>;
23
+ readonly page_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionPageId">]>;
24
24
  }, undefined>, v.ObjectSchema<{
25
25
  readonly type: v.LiteralSchema<"data_source_id", undefined>;
26
- readonly data_source_id: v.CustomSchema<import("../ids.js").NotionDataSourceId, v.ErrorMessage<v.CustomIssue> | undefined>;
26
+ readonly data_source_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionDataSourceId">]>;
27
27
  }, undefined>, v.ObjectSchema<{
28
28
  readonly type: v.LiteralSchema<"workspace", undefined>;
29
29
  readonly workspace: v.LiteralSchema<true, undefined>;
30
30
  }, undefined>, v.ObjectSchema<{
31
31
  readonly type: v.LiteralSchema<"block_id", undefined>;
32
- readonly block_id: v.CustomSchema<import("../ids.js").NotionBlockId, v.ErrorMessage<v.CustomIssue> | undefined>;
32
+ readonly block_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionBlockId">]>;
33
33
  }, undefined>, v.ObjectSchema<{
34
34
  readonly type: v.LiteralSchema<"agent_id", undefined>;
35
- readonly agent_id: v.CustomSchema<import("../ids.js").NotionAgentId, v.ErrorMessage<v.CustomIssue> | undefined>;
35
+ readonly agent_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionAgentId">]>;
36
36
  }, undefined>], undefined>;
37
37
  readonly properties: v.RecordSchema<v.StringSchema<undefined>, v.VariantSchema<"type", [v.ObjectSchema<{
38
38
  readonly type: v.LiteralSchema<"title", undefined>;
@@ -10,7 +10,7 @@ export type { NotionPageId } from "../ids.js";
10
10
  * The custom block's nearest enclosing page ancestor, carried in `init.page` and `pageChanged`.
11
11
  */
12
12
  export declare const customBlockPageSchema: v.ObjectSchema<{
13
- readonly id: v.CustomSchema<import("./page.js").NotionPageId, v.ErrorMessage<v.CustomIssue> | undefined>;
13
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionPageId">]>;
14
14
  /**
15
15
  * The containing page's own parent. It lets a block distinguish a freestanding page from a
16
16
  * database row without a round trip. Not to be confused with the top-level `parent` runtime
@@ -18,19 +18,19 @@ export declare const customBlockPageSchema: v.ObjectSchema<{
18
18
  */
19
19
  readonly parent: v.VariantSchema<"type", [v.ObjectSchema<{
20
20
  readonly type: v.LiteralSchema<"page_id", undefined>;
21
- readonly page_id: v.CustomSchema<import("./page.js").NotionPageId, v.ErrorMessage<v.CustomIssue> | undefined>;
21
+ readonly page_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionPageId">]>;
22
22
  }, undefined>, v.ObjectSchema<{
23
23
  readonly type: v.LiteralSchema<"data_source_id", undefined>;
24
- readonly data_source_id: v.CustomSchema<import("../ids.js").NotionDataSourceId, v.ErrorMessage<v.CustomIssue> | undefined>;
24
+ readonly data_source_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionDataSourceId">]>;
25
25
  }, undefined>, v.ObjectSchema<{
26
26
  readonly type: v.LiteralSchema<"workspace", undefined>;
27
27
  readonly workspace: v.LiteralSchema<true, undefined>;
28
28
  }, undefined>, v.ObjectSchema<{
29
29
  readonly type: v.LiteralSchema<"block_id", undefined>;
30
- readonly block_id: v.CustomSchema<import("../ids.js").NotionBlockId, v.ErrorMessage<v.CustomIssue> | undefined>;
30
+ readonly block_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionBlockId">]>;
31
31
  }, undefined>, v.ObjectSchema<{
32
32
  readonly type: v.LiteralSchema<"agent_id", undefined>;
33
- readonly agent_id: v.CustomSchema<import("../ids.js").NotionAgentId, v.ErrorMessage<v.CustomIssue> | undefined>;
33
+ readonly agent_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionAgentId">]>;
34
34
  }, undefined>], undefined>;
35
35
  }, undefined>;
36
36
  export type CustomBlockPage = v.InferOutput<typeof customBlockPageSchema>;
@@ -412,22 +412,22 @@ export type NotionPagePropertyWriteMap = v.InferOutput<typeof notionPageProperty
412
412
  */
413
413
  export declare const notionPageSchema: v.ObjectSchema<{
414
414
  readonly object: v.LiteralSchema<"page", undefined>;
415
- readonly id: v.CustomSchema<import("./page.js").NotionPageId, v.ErrorMessage<v.CustomIssue> | undefined>;
415
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionPageId">]>;
416
416
  readonly parent: v.VariantSchema<"type", [v.ObjectSchema<{
417
417
  readonly type: v.LiteralSchema<"page_id", undefined>;
418
- readonly page_id: v.CustomSchema<import("./page.js").NotionPageId, v.ErrorMessage<v.CustomIssue> | undefined>;
418
+ readonly page_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionPageId">]>;
419
419
  }, undefined>, v.ObjectSchema<{
420
420
  readonly type: v.LiteralSchema<"data_source_id", undefined>;
421
- readonly data_source_id: v.CustomSchema<import("../ids.js").NotionDataSourceId, v.ErrorMessage<v.CustomIssue> | undefined>;
421
+ readonly data_source_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionDataSourceId">]>;
422
422
  }, undefined>, v.ObjectSchema<{
423
423
  readonly type: v.LiteralSchema<"workspace", undefined>;
424
424
  readonly workspace: v.LiteralSchema<true, undefined>;
425
425
  }, undefined>, v.ObjectSchema<{
426
426
  readonly type: v.LiteralSchema<"block_id", undefined>;
427
- readonly block_id: v.CustomSchema<import("../ids.js").NotionBlockId, v.ErrorMessage<v.CustomIssue> | undefined>;
427
+ readonly block_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionBlockId">]>;
428
428
  }, undefined>, v.ObjectSchema<{
429
429
  readonly type: v.LiteralSchema<"agent_id", undefined>;
430
- readonly agent_id: v.CustomSchema<import("../ids.js").NotionAgentId, v.ErrorMessage<v.CustomIssue> | undefined>;
430
+ readonly agent_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionAgentId">]>;
431
431
  }, undefined>], undefined>;
432
432
  readonly properties: v.RecordSchema<v.StringSchema<undefined>, v.VariantSchema<"type", [v.ObjectSchema<{
433
433
  readonly type: v.LiteralSchema<"title", undefined>;
@@ -1,19 +1,19 @@
1
1
  import * as v from "valibot";
2
2
  export declare const notionParentSchema: v.VariantSchema<"type", [v.ObjectSchema<{
3
3
  readonly type: v.LiteralSchema<"page_id", undefined>;
4
- readonly page_id: v.CustomSchema<import("./ids.js").NotionPageId, v.ErrorMessage<v.CustomIssue> | undefined>;
4
+ readonly page_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionPageId">]>;
5
5
  }, undefined>, v.ObjectSchema<{
6
6
  readonly type: v.LiteralSchema<"data_source_id", undefined>;
7
- readonly data_source_id: v.CustomSchema<import("./ids.js").NotionDataSourceId, v.ErrorMessage<v.CustomIssue> | undefined>;
7
+ readonly data_source_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionDataSourceId">]>;
8
8
  }, undefined>, v.ObjectSchema<{
9
9
  readonly type: v.LiteralSchema<"workspace", undefined>;
10
10
  readonly workspace: v.LiteralSchema<true, undefined>;
11
11
  }, undefined>, v.ObjectSchema<{
12
12
  readonly type: v.LiteralSchema<"block_id", undefined>;
13
- readonly block_id: v.CustomSchema<import("./ids.js").NotionBlockId, v.ErrorMessage<v.CustomIssue> | undefined>;
13
+ readonly block_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionBlockId">]>;
14
14
  }, undefined>, v.ObjectSchema<{
15
15
  readonly type: v.LiteralSchema<"agent_id", undefined>;
16
- readonly agent_id: v.CustomSchema<import("./ids.js").NotionAgentId, v.ErrorMessage<v.CustomIssue> | undefined>;
16
+ readonly agent_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionAgentId">]>;
17
17
  }, undefined>], undefined>;
18
18
  /**
19
19
  * A record's parent reference.
@@ -1,11 +1,9 @@
1
1
  import * as v from "valibot";
2
- declare const notionUserIdBrand: unique symbol;
3
- export type NotionUserId = string & {
4
- readonly [notionUserIdBrand]: "NotionUserId";
5
- };
2
+ export declare const notionUserIdSchema: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionUserId">]>;
3
+ export type NotionUserId = v.InferOutput<typeof notionUserIdSchema>;
6
4
  export declare const notionUserSchema: v.ObjectSchema<{
7
5
  readonly object: v.LiteralSchema<"user", undefined>;
8
- readonly id: v.StringSchema<undefined>;
6
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionUserId">]>;
9
7
  readonly name: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
10
8
  readonly avatar_url: v.NullableSchema<v.StringSchema<undefined>, undefined>;
11
9
  readonly type: v.LiteralSchema<"person", undefined>;
@@ -18,7 +16,7 @@ export declare const notionUserListSchema: v.ObjectSchema<{
18
16
  readonly object: v.LiteralSchema<"list", undefined>;
19
17
  readonly results: v.ArraySchema<v.ObjectSchema<{
20
18
  readonly object: v.LiteralSchema<"user", undefined>;
21
- readonly id: v.StringSchema<undefined>;
19
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.BrandAction<string, "NotionUserId">]>;
22
20
  readonly name: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
23
21
  readonly avatar_url: v.NullableSchema<v.StringSchema<undefined>, undefined>;
24
22
  readonly type: v.LiteralSchema<"person", undefined>;
@@ -32,5 +30,4 @@ export declare const notionUserListSchema: v.ObjectSchema<{
32
30
  readonly user: v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>;
33
31
  }, undefined>;
34
32
  export type NotionUserList = v.InferOutput<typeof notionUserListSchema>;
35
- export {};
36
33
  //# sourceMappingURL=user.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/protocol/src/users/user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B,OAAO,CAAC,MAAM,iBAAiB,EAAE,OAAO,MAAM,CAAA;AAE9C,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IACnC,QAAQ,CAAC,CAAC,iBAAiB,CAAC,EAAE,cAAc,CAAA;CAC5C,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;aAS3B,CAAA;AAEF,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAE/D,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;aAO/B,CAAA;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,oBAAoB,CAAC,CAAA"}
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../../../home/runner/work/custom-blocks/custom-blocks/protocol/src/users/user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B,eAAO,MAAM,kBAAkB,+FAA8C,CAAA;AAC7E,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAEnE,eAAO,MAAM,gBAAgB;;;;;;;;;aAS3B,CAAA;AAEF,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAE/D,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;aAO/B,CAAA;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,oBAAoB,CAAC,CAAA"}
@@ -1,7 +1,8 @@
1
1
  import * as v from "valibot";
2
+ export const notionUserIdSchema = v.pipe(v.string(), v.brand("NotionUserId"));
2
3
  export const notionUserSchema = v.object({
3
4
  object: v.literal("user"),
4
- id: v.string(),
5
+ id: notionUserIdSchema,
5
6
  name: v.optional(v.string()),
6
7
  avatar_url: v.nullable(v.string()),
7
8
  type: v.literal("person"),
@@ -1 +1 @@
1
- {"version":3,"file":"standalonePreview.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/react/standalonePreview.ts"],"names":[],"mappings":"AAWA,wBAAgB,0BAA0B,SAuBzC"}
1
+ {"version":3,"file":"standalonePreview.d.ts","sourceRoot":"","sources":["../../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/react/standalonePreview.ts"],"names":[],"mappings":"AAaA,wBAAgB,0BAA0B,SAuBzC"}
@@ -1,9 +1,11 @@
1
1
  import { DEFAULT_CONTRAST_MODE } from "../protocol/contrast.js";
2
2
  import { notionBlockIdSchema, notionPageIdSchema, } from "../protocol/ids.js";
3
+ import { notionUserIdSchema } from "../protocol/users/user.js";
3
4
  import * as v from "valibot";
4
5
  import { customBlockHost } from "../bridge/sandboxClient.js";
5
6
  const previewBlockId = v.parse(notionBlockIdSchema, "");
6
7
  const previewPageId = v.parse(notionPageIdSchema, "");
8
+ const previewUserId = v.parse(notionUserIdSchema, "");
7
9
  export function seedStandalonePreviewState() {
8
10
  customBlockHost.setMockState({
9
11
  type: "init",
@@ -18,7 +20,7 @@ export function seedStandalonePreviewState() {
18
20
  dataSources: { bindings: {} },
19
21
  currentUser: {
20
22
  object: "user",
21
- id: "",
23
+ id: previewUserId,
22
24
  name: "Preview User",
23
25
  avatar_url: null,
24
26
  type: "person",
package/dist/version.js CHANGED
@@ -4,4 +4,4 @@
4
4
  *
5
5
  * WARNING: Generated during SDK publish. Do not edit in the published package.
6
6
  */
7
- export const CUSTOM_BLOCKS_SDK_VERSION = "0.1.17"
7
+ export const CUSTOM_BLOCKS_SDK_VERSION = "0.1.19"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/custom-blocks",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -1,3 +1,4 @@
1
+ import type { NotionContrastMode } from "@notionhq/custom-blocks-protocol/contrast.js"
1
2
  import { DEFAULT_CONTRAST_MODE } from "@notionhq/custom-blocks-protocol/contrast.js"
2
3
  import type {
3
4
  NotionDataSource,
@@ -29,6 +30,7 @@ import type { CustomBlockQueryDataSourceErrorInfo } from "@notionhq/custom-block
29
30
  import type { ResizeMessage } from "@notionhq/custom-blocks-protocol/messages/resize.js"
30
31
  import type { UpdatePageMessage } from "@notionhq/custom-blocks-protocol/messages/updatePage.js"
31
32
  import { CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION } from "@notionhq/custom-blocks-protocol/protocolVersion.js"
33
+ import type { NotionTheme } from "@notionhq/custom-blocks-protocol/theme.js"
32
34
  import type { NotionUser } from "@notionhq/custom-blocks-protocol/users/user.js"
33
35
  import * as v from "valibot"
34
36
  import type {
@@ -48,6 +50,10 @@ import type {
48
50
  } from "../types.js"
49
51
  import { unreachable } from "../utils.js"
50
52
  import { CUSTOM_BLOCKS_SDK_VERSION } from "../version.js"
53
+ import {
54
+ NOTION_DARK_BACKGROUND_BASE,
55
+ NOTION_LIGHT_BACKGROUND_BASE,
56
+ } from "./appearance.js"
51
57
  import { resolveDataSources } from "./dataSources/resolve.js"
52
58
  import { resolvePropertyWriteMapForDataSource } from "./dataSources/resolveProperty.js"
53
59
  import {
@@ -98,6 +104,7 @@ export class SandboxBridge {
98
104
  private initializationId: string | undefined
99
105
  private latestDataSourceBindings: NotionDataSourceBindings = {}
100
106
  private isMockState = false
107
+ private isListening = false
101
108
  private resolveInit: (() => void) | undefined
102
109
  private rejectInit: ((reason: Error) => void) | undefined
103
110
  private readonly initMessage: Promise<void> = new Promise(
@@ -107,13 +114,12 @@ export class SandboxBridge {
107
114
  },
108
115
  )
109
116
 
110
- constructor() {
111
- // `connect` is sent later by `initCustomBlock` (after the manifest fetch
112
- // resolves). Top-level / no-iframe rejection is handled there too, so
113
- // the constructor just attaches the listener.
114
- if (typeof window !== "undefined") {
115
- window.addEventListener("message", this.handleMessage)
117
+ startListening() {
118
+ if (this.isListening || typeof window === "undefined") {
119
+ return
116
120
  }
121
+ this.isListening = true
122
+ window.addEventListener("message", this.handleMessage)
117
123
  }
118
124
 
119
125
  private static MAX_LOG_ENTRIES = 100
@@ -291,6 +297,10 @@ export class SandboxBridge {
291
297
  ...hostState,
292
298
  theme: message.theme,
293
299
  }
300
+ syncDocumentAppearance({
301
+ theme: message.theme,
302
+ contrastMode: hostState.contrastMode,
303
+ })
294
304
  this.notify()
295
305
  return
296
306
  }
@@ -300,6 +310,10 @@ export class SandboxBridge {
300
310
  ...hostState,
301
311
  contrastMode: message.contrastMode,
302
312
  }
313
+ syncDocumentAppearance({
314
+ theme: hostState.theme,
315
+ contrastMode: message.contrastMode,
316
+ })
303
317
  this.notify()
304
318
  return
305
319
  }
@@ -550,6 +564,10 @@ export class SandboxBridge {
550
564
  dataSources,
551
565
  dataSourceState: {},
552
566
  }
567
+ syncDocumentAppearance({
568
+ theme: message.theme,
569
+ contrastMode: message.contrastMode,
570
+ })
553
571
  this.notify()
554
572
  if (postResult) {
555
573
  const result: InitResultMessage = {
@@ -885,6 +903,31 @@ export class SandboxBridge {
885
903
  }
886
904
  }
887
905
 
906
+ /**
907
+ * Makes the document canvas match the host appearance before acknowledging init.
908
+ *
909
+ * The host reveals the iframe as soon as it receives `initResult.success`. Applying these
910
+ * attributes synchronously prevents the browser's default white canvas from flashing while React
911
+ * mounts a token scope.
912
+ */
913
+ function syncDocumentAppearance(args: {
914
+ theme: NotionTheme
915
+ contrastMode: NotionContrastMode
916
+ }): void {
917
+ if (typeof document === "undefined") {
918
+ return
919
+ }
920
+ const { theme, contrastMode } = args
921
+ const root = document.documentElement
922
+ root.dataset.displayMode = theme
923
+ root.dataset.contrastMode = contrastMode
924
+ root.style.colorScheme = theme
925
+ root.style.backgroundColor =
926
+ theme === "dark"
927
+ ? NOTION_DARK_BACKGROUND_BASE
928
+ : NOTION_LIGHT_BACKGROUND_BASE
929
+ }
930
+
888
931
  /**
889
932
  * `postMessage` cloning creates new objects even for bindings that did not change.
890
933
  * Preserve their resolved object identity so React effects only run for changed bindings.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Background color values used before the NDS token scope mounts.
3
+ *
4
+ * The light value matches NDS's semantic `--bg-base` token in light mode
5
+ * (`--gray-0`, white). The dark value is the existing SDK fallback canvas
6
+ * color; it is not an exact current generated NDS value. Current NDS maps
7
+ * dark `--bg-base` to `--gray-145` (approximately `#1b1b1b`), while the
8
+ * legacy notion-next gray surface is `rgba(32, 32, 32, 1)`.
9
+ *
10
+ * Keep the resolved values here because the document must be styled before the block's NDS
11
+ * stylesheet and token scope are guaranteed to be available.
12
+ */
13
+ export const NOTION_DARK_BACKGROUND_BASE = "#191918"
14
+ export const NOTION_LIGHT_BACKGROUND_BASE = "#ffffff"
@@ -22,23 +22,34 @@ import { type MessageLogEntry, SandboxBridge } from "./SandboxBridge.js"
22
22
 
23
23
  export type { MessageLogEntry }
24
24
 
25
- const bridge = new SandboxBridge()
25
+ let bridge: SandboxBridge | undefined
26
+
27
+ function getBridge(): SandboxBridge {
28
+ if (!bridge) {
29
+ bridge = new SandboxBridge()
30
+ }
31
+ return bridge
32
+ }
26
33
 
27
34
  export const customBlockHost = {
35
+ start: () => {
36
+ getBridge().startListening()
37
+ },
38
+
28
39
  sendConnect: (manifestResult: ManifestLoadResult) => {
29
- bridge.sendConnect(manifestResult)
40
+ getBridge().sendConnect(manifestResult)
30
41
  },
31
42
 
32
43
  awaitInit: (signal?: AbortSignal): Promise<void> => {
33
- return bridge.awaitInit(signal)
44
+ return getBridge().awaitInit(signal)
34
45
  },
35
46
 
36
47
  subscribe: (listener: () => void) => {
37
- return bridge.subscribe(listener)
48
+ return getBridge().subscribe(listener)
38
49
  },
39
50
 
40
51
  getState: (): CustomBlockHostState => {
41
- return bridge.getHostState()
52
+ return getBridge().getHostState()
42
53
  },
43
54
 
44
55
  /**
@@ -46,17 +57,17 @@ export const customBlockHost = {
46
57
  * standalone preview state.
47
58
  */
48
59
  setMockState: (message: InitMessage) => {
49
- bridge.setMockState(message)
60
+ getBridge().setMockState(message)
50
61
  },
51
62
 
52
63
  postResize: (height: number) => {
53
- bridge.postResize(height)
64
+ getBridge().postResize(height)
54
65
  },
55
66
  }
56
67
 
57
68
  export const customBlockDataSources = {
58
69
  query: (key: string, options?: UseDataSourceOptions) => {
59
- bridge.queryDataSource(key, options)
70
+ getBridge().queryDataSource(key, options)
60
71
  },
61
72
 
62
73
  getView: (
@@ -64,18 +75,18 @@ export const customBlockDataSources = {
64
75
  key: string,
65
76
  ): DataSourceQueryView => {
66
77
  return getDataSourceQueryViewWithBridge(hostState, key, args =>
67
- bridge.updateDataSourcePage(args),
78
+ getBridge().updateDataSourcePage(args),
68
79
  )
69
80
  },
70
81
  }
71
82
 
72
83
  export const messageLog = {
73
84
  getSnapshot: (): readonly MessageLogEntry[] => {
74
- return bridge.getMessageLog()
85
+ return getBridge().getMessageLog()
75
86
  },
76
87
 
77
88
  subscribe: (listener: () => void) => {
78
- return bridge.subscribeToMessageLog(listener)
89
+ return getBridge().subscribeToMessageLog(listener)
79
90
  },
80
91
  }
81
92
 
@@ -87,28 +98,28 @@ export const pages = {
87
98
  * Creates a new Notion page.
88
99
  */
89
100
  create: (input: CreatePageArgs): Promise<CreatePageResult> => {
90
- return bridge.createPage(input)
101
+ return getBridge().createPage(input)
91
102
  },
92
103
 
93
104
  /**
94
105
  * Fetches a page by id.
95
106
  */
96
107
  get: (pageId: NotionPageId): Promise<GetPageResult> => {
97
- return bridge.getPage(pageId)
108
+ return getBridge().getPage(pageId)
98
109
  },
99
110
 
100
111
  /**
101
112
  * Updates an existing page.
102
113
  */
103
114
  update: (input: UpdatePageArgs): Promise<UpdatePageResult> => {
104
- return bridge.updatePage(input)
115
+ return getBridge().updatePage(input)
105
116
  },
106
117
 
107
118
  /**
108
119
  * Soft-delete a page by archiving it.
109
120
  */
110
121
  delete: (pageId: NotionPageId): Promise<UpdatePageResult> => {
111
- return bridge.updatePage({ pageId, archived: true })
122
+ return getBridge().updatePage({ pageId, archived: true })
112
123
  },
113
124
  }
114
125
 
@@ -120,13 +131,13 @@ export const users = {
120
131
  * Lists users visible in the current custom block workspace.
121
132
  */
122
133
  list: (input?: ListUsersArgs): Promise<ListUsersResult> => {
123
- return bridge.listUsers(input)
134
+ return getBridge().listUsers(input)
124
135
  },
125
136
 
126
137
  /**
127
138
  * Fetches a user by id.
128
139
  */
129
140
  get: (userId: NotionUserId): Promise<GetUserResult> => {
130
- return bridge.getUser(userId)
141
+ return getBridge().getUser(userId)
131
142
  },
132
143
  }
package/src/init.ts CHANGED
@@ -76,6 +76,7 @@ export function initCustomBlock(
76
76
  opts: InitCustomBlockOptions = {},
77
77
  ): Promise<CustomBlockInitPayload> {
78
78
  if (initPromise === undefined) {
79
+ customBlockHost.start()
79
80
  initPromise = performHandshake(opts)
80
81
  }
81
82
  return initPromise
@@ -3,11 +3,13 @@ import {
3
3
  notionBlockIdSchema,
4
4
  notionPageIdSchema,
5
5
  } from "@notionhq/custom-blocks-protocol/ids.js"
6
+ import { notionUserIdSchema } from "@notionhq/custom-blocks-protocol/users/user.js"
6
7
  import * as v from "valibot"
7
8
  import { customBlockHost } from "../bridge/sandboxClient.js"
8
9
 
9
10
  const previewBlockId = v.parse(notionBlockIdSchema, "")
10
11
  const previewPageId = v.parse(notionPageIdSchema, "")
12
+ const previewUserId = v.parse(notionUserIdSchema, "")
11
13
 
12
14
  export function seedStandalonePreviewState() {
13
15
  customBlockHost.setMockState({
@@ -23,7 +25,7 @@ export function seedStandalonePreviewState() {
23
25
  dataSources: { bindings: {} },
24
26
  currentUser: {
25
27
  object: "user",
26
- id: "",
28
+ id: previewUserId,
27
29
  name: "Preview User",
28
30
  avatar_url: null,
29
31
  type: "person",