@notionhq/custom-blocks 0.0.63 → 0.0.64
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/README.md +2 -2
- package/bin/cli/create.js +3 -3
- package/dist/bridge/SandboxBridge.js +17 -17
- package/dist/bridge/loadManifest.js +6 -6
- package/dist/init.js +1 -1
- package/dist/react/NotionCustomBlock.css +47 -47
- package/dist/react/NotionCustomBlock.js +4 -4
- package/dist/utils.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/docs/block-location.md +1 -1
- package/docs/manifest.md +1 -1
- package/docs/pages.md +1 -1
- package/package.json +1 -1
- package/src/bridge/SandboxBridge.ts +17 -17
- package/src/bridge/loadManifest.ts +6 -6
- package/src/init.ts +1 -1
- package/src/react/NotionCustomBlock.css +47 -47
- package/src/react/NotionCustomBlock.tsx +14 -10
- package/src/utils.ts +1 -1
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
> [!NOTE]
|
|
4
4
|
> Nothing to see here. This is **extreme** alpha and currently only works for an unreleased product.
|
|
5
5
|
|
|
6
|
-
SDK for building Notion custom
|
|
6
|
+
SDK for building Notion custom blocks.
|
|
7
7
|
|
|
8
|
-
A custom
|
|
8
|
+
A custom block runs as a sandboxed `<iframe>` inside a Notion block on iOS, Android, and desktop. The only channel between your block and Notion is a `postMessage` bridge — this SDK wraps it in typed React hooks plus a small framework-neutral runtime API for non-React renderers.
|
|
9
9
|
|
|
10
10
|
> **Pre-release.** Breaking changes may land at any time before 1.0.
|
|
11
11
|
|
package/bin/cli/create.js
CHANGED
|
@@ -69,7 +69,7 @@ function createCustomBlock(pageId) {
|
|
|
69
69
|
*
|
|
70
70
|
* - No `--parent` → create a private page, then a block under it.
|
|
71
71
|
* - Page `--parent` → create a block under that page.
|
|
72
|
-
* - Database/data source `--parent` → a custom
|
|
72
|
+
* - Database/data source `--parent` → a custom block as a collection view. The
|
|
73
73
|
* public API has no `custom` view type and the parent-nesting the backend
|
|
74
74
|
* uses isn't derivable from this repo, so we error rather than guess.
|
|
75
75
|
*/
|
|
@@ -82,8 +82,8 @@ export function create(options) {
|
|
|
82
82
|
const parentUuid = formatUuid(extractId(options.parent));
|
|
83
83
|
const kind = classifyParent(parentUuid);
|
|
84
84
|
if (kind === "database" || kind === "data_source") {
|
|
85
|
-
console.error(`Error: --parent ${parentUuid} is a ${kind.replace("_", " ")}. Creating a custom
|
|
86
|
-
console.error(" The public API has no `custom` view type; the backend does the parent nesting. Create the
|
|
85
|
+
console.error(`Error: --parent ${parentUuid} is a ${kind.replace("_", " ")}. Creating a custom block as a collection view isn't supported yet.\n`);
|
|
86
|
+
console.error(" The public API has no `custom` view type; the backend does the parent nesting. Create the custom block in Notion, then run `ncblock attach <block-id-or-url>`.");
|
|
87
87
|
process.exit(1);
|
|
88
88
|
}
|
|
89
89
|
pageId = parentUuid;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as v from "valibot";
|
|
2
2
|
import { unreachable } from "../utils.js";
|
|
3
|
-
import {
|
|
3
|
+
import { CUSTOM_BLOCKS_SDK_VERSION } from "../version.js";
|
|
4
4
|
import { resolveDataSources } from "./dataSources/resolve.js";
|
|
5
5
|
import { resolvePropertyWriteMapForDataSource } from "./dataSources/resolveProperty.js";
|
|
6
6
|
import { createEmptyDataSourceQueryState, } from "./hostState.js";
|
|
@@ -43,7 +43,7 @@ export class SandboxBridge {
|
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
this.handleMessage = (event) => {
|
|
46
|
-
console.debug("[
|
|
46
|
+
console.debug("[custom-blocks-sdk] incoming postMessage", {
|
|
47
47
|
data: event.data,
|
|
48
48
|
fromParent: event.source === window.parent,
|
|
49
49
|
});
|
|
@@ -53,7 +53,7 @@ export class SandboxBridge {
|
|
|
53
53
|
this.logMessage("received", event.data);
|
|
54
54
|
const parsed = v.safeParse(hostToSandboxMessageSchema, event.data);
|
|
55
55
|
if (!parsed.success) {
|
|
56
|
-
console.warn("[
|
|
56
|
+
console.warn("[custom-blocks-sdk] ignoring malformed host message", parsed.issues);
|
|
57
57
|
const incomingType = readIncomingType(event.data);
|
|
58
58
|
// Loop guard: never NACK a NACK. Excludes both directions —
|
|
59
59
|
// `invalidSandboxMessage` is what the host normally sends, but
|
|
@@ -75,7 +75,7 @@ export class SandboxBridge {
|
|
|
75
75
|
// visibility and stop. We don't retry as the sandbox can't recover from a host-side parse
|
|
76
76
|
// failure on its own.
|
|
77
77
|
if (message.type === "invalidSandboxMessage") {
|
|
78
|
-
console.warn("[
|
|
78
|
+
console.warn("[custom-blocks-sdk] host reported invalid sandbox message:", message.reason);
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
// `init` is the only message valid before initialization. Handle it up
|
|
@@ -88,7 +88,7 @@ export class SandboxBridge {
|
|
|
88
88
|
// below. Reading `this.hostState` repeatedly would re-widen it.
|
|
89
89
|
const hostState = this.hostState;
|
|
90
90
|
if (hostState.status !== "initialized") {
|
|
91
|
-
console.warn(`[
|
|
91
|
+
console.warn(`[custom-blocks-sdk] ignoring ${message.type} before init`);
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
94
|
switch (message.type) {
|
|
@@ -157,7 +157,7 @@ export class SandboxBridge {
|
|
|
157
157
|
? { status: "success", page: message.page }
|
|
158
158
|
: { status: "error", error: message.error };
|
|
159
159
|
if (!this.pendingCreatePage.resolve(message.requestId, result)) {
|
|
160
|
-
console.warn(`[
|
|
160
|
+
console.warn(`[custom-blocks-sdk] createPageResult for unknown requestId ${message.requestId}`);
|
|
161
161
|
}
|
|
162
162
|
return;
|
|
163
163
|
}
|
|
@@ -166,7 +166,7 @@ export class SandboxBridge {
|
|
|
166
166
|
? { status: "success", page: message.page }
|
|
167
167
|
: { status: "error", error: message.error };
|
|
168
168
|
if (!this.pendingGetPage.resolve(message.requestId, result)) {
|
|
169
|
-
console.warn(`[
|
|
169
|
+
console.warn(`[custom-blocks-sdk] getPageResult for unknown requestId ${message.requestId}`);
|
|
170
170
|
}
|
|
171
171
|
return;
|
|
172
172
|
}
|
|
@@ -175,7 +175,7 @@ export class SandboxBridge {
|
|
|
175
175
|
? { status: "success", user: message.user }
|
|
176
176
|
: { status: "error", error: message.error };
|
|
177
177
|
if (!this.pendingGetUser.resolve(message.requestId, result)) {
|
|
178
|
-
console.warn(`[
|
|
178
|
+
console.warn(`[custom-blocks-sdk] getUserResult for unknown requestId ${message.requestId}`);
|
|
179
179
|
}
|
|
180
180
|
return;
|
|
181
181
|
}
|
|
@@ -184,7 +184,7 @@ export class SandboxBridge {
|
|
|
184
184
|
? { status: "success", list: message.list }
|
|
185
185
|
: { status: "error", error: message.error };
|
|
186
186
|
if (!this.pendingListUsers.resolve(message.requestId, result)) {
|
|
187
|
-
console.warn(`[
|
|
187
|
+
console.warn(`[custom-blocks-sdk] listUsersResult for unknown requestId ${message.requestId}`);
|
|
188
188
|
}
|
|
189
189
|
return;
|
|
190
190
|
}
|
|
@@ -193,7 +193,7 @@ export class SandboxBridge {
|
|
|
193
193
|
? { status: "success", page: message.page }
|
|
194
194
|
: { status: "error", error: message.error };
|
|
195
195
|
if (!this.pendingUpdatePage.resolve(message.requestId, result)) {
|
|
196
|
-
console.warn(`[
|
|
196
|
+
console.warn(`[custom-blocks-sdk] updatePageResult for unknown requestId ${message.requestId}`);
|
|
197
197
|
}
|
|
198
198
|
return;
|
|
199
199
|
}
|
|
@@ -300,7 +300,7 @@ export class SandboxBridge {
|
|
|
300
300
|
return;
|
|
301
301
|
}
|
|
302
302
|
if (this.hasSentReady) {
|
|
303
|
-
console.warn("[
|
|
303
|
+
console.warn("[custom-blocks-sdk] ignoring duplicate ready message");
|
|
304
304
|
return;
|
|
305
305
|
}
|
|
306
306
|
const { manifest, error } = manifestResult;
|
|
@@ -311,20 +311,20 @@ export class SandboxBridge {
|
|
|
311
311
|
type: "ready",
|
|
312
312
|
status: "error",
|
|
313
313
|
bridgeProtocolVersion: CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION,
|
|
314
|
-
sdkVersion:
|
|
314
|
+
sdkVersion: CUSTOM_BLOCKS_SDK_VERSION,
|
|
315
315
|
error,
|
|
316
316
|
}
|
|
317
317
|
: {
|
|
318
318
|
type: "ready",
|
|
319
319
|
status: "success",
|
|
320
320
|
bridgeProtocolVersion: CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION,
|
|
321
|
-
sdkVersion:
|
|
321
|
+
sdkVersion: CUSTOM_BLOCKS_SDK_VERSION,
|
|
322
322
|
manifest,
|
|
323
323
|
};
|
|
324
324
|
this.postToHost(readyMessage);
|
|
325
325
|
}
|
|
326
326
|
postToHost(message) {
|
|
327
|
-
console.debug("[
|
|
327
|
+
console.debug("[custom-blocks-sdk] outbound postMessage", message);
|
|
328
328
|
this.logMessage("sent", message);
|
|
329
329
|
window.parent.postMessage(message, "*");
|
|
330
330
|
}
|
|
@@ -361,7 +361,7 @@ export class SandboxBridge {
|
|
|
361
361
|
// because not every consumer renders the error UI, and we want the failure visible in
|
|
362
362
|
// the browser console either way. We deliberately don't NACK the host or post anything
|
|
363
363
|
// back over the bridge since the host has already given up.
|
|
364
|
-
console.error(`[
|
|
364
|
+
console.error(`[custom-blocks-sdk] host reported init error (${message.error.code}): ${message.error.message}`);
|
|
365
365
|
if (this.rejectInit) {
|
|
366
366
|
this.rejectInit(new CustomBlockInitError(message.error));
|
|
367
367
|
this.resolveInit = undefined;
|
|
@@ -705,11 +705,11 @@ function resolveDataSourceQueryLimit(limit) {
|
|
|
705
705
|
return DEFAULT_DATA_SOURCE_QUERY_LIMIT;
|
|
706
706
|
}
|
|
707
707
|
if (!Number.isFinite(limit) || !Number.isInteger(limit) || limit < 1) {
|
|
708
|
-
console.warn(`[
|
|
708
|
+
console.warn(`[custom-blocks-sdk] useDataSource limit must be a positive integer; using ${DEFAULT_DATA_SOURCE_QUERY_LIMIT}.`);
|
|
709
709
|
return DEFAULT_DATA_SOURCE_QUERY_LIMIT;
|
|
710
710
|
}
|
|
711
711
|
if (limit > MAX_DATA_SOURCE_QUERY_LIMIT) {
|
|
712
|
-
console.warn(`[
|
|
712
|
+
console.warn(`[custom-blocks-sdk] useDataSource limit is capped at ${MAX_DATA_SOURCE_QUERY_LIMIT}.`);
|
|
713
713
|
return MAX_DATA_SOURCE_QUERY_LIMIT;
|
|
714
714
|
}
|
|
715
715
|
return limit;
|
|
@@ -18,7 +18,7 @@ export const customBlockReadyErrorSchema = v.object({
|
|
|
18
18
|
export async function loadManifest() {
|
|
19
19
|
if (typeof fetch !== "function") {
|
|
20
20
|
const message = `No fetch API available; cannot load ${MANIFEST_URL}.`;
|
|
21
|
-
console.warn(`[
|
|
21
|
+
console.warn(`[custom-blocks-sdk] ${message}`);
|
|
22
22
|
return {
|
|
23
23
|
manifest: null,
|
|
24
24
|
error: { code: "manifest_unavailable", message },
|
|
@@ -30,7 +30,7 @@ export async function loadManifest() {
|
|
|
30
30
|
}
|
|
31
31
|
catch (error) {
|
|
32
32
|
const message = `Could not fetch ${MANIFEST_URL}.`;
|
|
33
|
-
console.warn(`[
|
|
33
|
+
console.warn(`[custom-blocks-sdk] ${message}`, error);
|
|
34
34
|
return {
|
|
35
35
|
manifest: null,
|
|
36
36
|
error: { code: "manifest_unavailable", message },
|
|
@@ -38,12 +38,12 @@ export async function loadManifest() {
|
|
|
38
38
|
}
|
|
39
39
|
if (response.status === 404) {
|
|
40
40
|
const message = `No manifest found at ${MANIFEST_URL} (status ${response.status}).`;
|
|
41
|
-
console.warn(`[
|
|
41
|
+
console.warn(`[custom-blocks-sdk] ${message}`);
|
|
42
42
|
return { manifest: null };
|
|
43
43
|
}
|
|
44
44
|
if (!response.ok) {
|
|
45
45
|
const message = `Could not fetch ${MANIFEST_URL} (status ${response.status}).`;
|
|
46
|
-
console.warn(`[
|
|
46
|
+
console.warn(`[custom-blocks-sdk] ${message}`);
|
|
47
47
|
return {
|
|
48
48
|
manifest: null,
|
|
49
49
|
error: { code: "manifest_unavailable", message },
|
|
@@ -55,13 +55,13 @@ export async function loadManifest() {
|
|
|
55
55
|
}
|
|
56
56
|
catch (error) {
|
|
57
57
|
const message = `Manifest at ${MANIFEST_URL} was not valid JSON.`;
|
|
58
|
-
console.warn(`[
|
|
58
|
+
console.warn(`[custom-blocks-sdk] ${message}`, error);
|
|
59
59
|
return { manifest: null, error: { code: "manifest_invalid", message } };
|
|
60
60
|
}
|
|
61
61
|
const parsed = v.safeParse(manifestSchema, json);
|
|
62
62
|
if (!parsed.success) {
|
|
63
63
|
const message = `Manifest at ${MANIFEST_URL} did not match schema.`;
|
|
64
|
-
console.warn(`[
|
|
64
|
+
console.warn(`[custom-blocks-sdk] ${message}`, parsed.issues);
|
|
65
65
|
return { manifest: null, error: { code: "manifest_invalid", message } };
|
|
66
66
|
}
|
|
67
67
|
return { manifest: parsed.output };
|
package/dist/init.js
CHANGED
|
@@ -56,7 +56,7 @@ export function initCustomBlock(opts = {}) {
|
|
|
56
56
|
throw error;
|
|
57
57
|
}
|
|
58
58
|
if (message.status === "error") {
|
|
59
|
-
console.error(`[
|
|
59
|
+
console.error(`[custom-blocks-sdk] host reported init error (${message.error.code}): ${message.error.message}`);
|
|
60
60
|
throw new CustomBlockInitError(message.error);
|
|
61
61
|
}
|
|
62
62
|
const hostState = customBlockHost.getState();
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
.
|
|
2
|
-
--
|
|
3
|
-
--
|
|
4
|
-
--
|
|
1
|
+
.custom-blocks-standalone-banner {
|
|
2
|
+
--custom-blocks-standalone-banner-background: #fff8e1;
|
|
3
|
+
--custom-blocks-standalone-banner-border: #f0d77b;
|
|
4
|
+
--custom-blocks-standalone-banner-text: #5d4200;
|
|
5
5
|
|
|
6
6
|
padding: 8px 12px;
|
|
7
|
-
background: var(--
|
|
8
|
-
color: var(--
|
|
9
|
-
border-bottom: 1px solid var(--
|
|
7
|
+
background: var(--custom-blocks-standalone-banner-background);
|
|
8
|
+
color: var(--custom-blocks-standalone-banner-text);
|
|
9
|
+
border-bottom: 1px solid var(--custom-blocks-standalone-banner-border);
|
|
10
10
|
font-size: 13px;
|
|
11
11
|
font-family: system-ui, sans-serif;
|
|
12
12
|
line-height: 1.4;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
.
|
|
16
|
-
--
|
|
17
|
-
--
|
|
18
|
-
--
|
|
19
|
-
--
|
|
20
|
-
--
|
|
21
|
-
--
|
|
22
|
-
--
|
|
23
|
-
--
|
|
24
|
-
--
|
|
15
|
+
.custom-blocks-default-init-error {
|
|
16
|
+
--custom-blocks-init-error-background: #f7f7f5;
|
|
17
|
+
--custom-blocks-init-error-card-background: #ffffff;
|
|
18
|
+
--custom-blocks-init-error-border: #e5e5e1;
|
|
19
|
+
--custom-blocks-init-error-text: #1f1f1d;
|
|
20
|
+
--custom-blocks-init-error-muted-text: #5f5e5b;
|
|
21
|
+
--custom-blocks-init-error-eyebrow-text: #787774;
|
|
22
|
+
--custom-blocks-init-error-pre-background: #f1f1ef;
|
|
23
|
+
--custom-blocks-init-error-pre-text: #37352f;
|
|
24
|
+
--custom-blocks-init-error-shadow: rgba(0, 0, 0, 0.08);
|
|
25
25
|
|
|
26
26
|
box-sizing: border-box;
|
|
27
27
|
min-height: 240px;
|
|
@@ -29,81 +29,81 @@
|
|
|
29
29
|
align-items: center;
|
|
30
30
|
justify-content: center;
|
|
31
31
|
padding: 24px;
|
|
32
|
-
background: var(--
|
|
33
|
-
color: var(--
|
|
32
|
+
background: var(--custom-blocks-init-error-background);
|
|
33
|
+
color: var(--custom-blocks-init-error-text);
|
|
34
34
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
|
35
35
|
color-scheme: light;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
.
|
|
38
|
+
.custom-blocks-default-init-error__card {
|
|
39
39
|
box-sizing: border-box;
|
|
40
40
|
width: 100%;
|
|
41
41
|
max-width: 420px;
|
|
42
42
|
padding: 20px;
|
|
43
|
-
border: 1px solid var(--
|
|
43
|
+
border: 1px solid var(--custom-blocks-init-error-border);
|
|
44
44
|
border-radius: 12px;
|
|
45
|
-
background: var(--
|
|
46
|
-
box-shadow: 0 8px 24px var(--
|
|
45
|
+
background: var(--custom-blocks-init-error-card-background);
|
|
46
|
+
box-shadow: 0 8px 24px var(--custom-blocks-init-error-shadow);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
.
|
|
49
|
+
.custom-blocks-default-init-error__eyebrow {
|
|
50
50
|
margin-bottom: 8px;
|
|
51
51
|
font-size: 11px;
|
|
52
52
|
font-weight: 600;
|
|
53
53
|
letter-spacing: 0.08em;
|
|
54
54
|
text-transform: uppercase;
|
|
55
|
-
color: var(--
|
|
55
|
+
color: var(--custom-blocks-init-error-eyebrow-text);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
.
|
|
58
|
+
.custom-blocks-default-init-error__title {
|
|
59
59
|
margin: 0;
|
|
60
60
|
font-size: 18px;
|
|
61
61
|
line-height: 1.3;
|
|
62
62
|
font-weight: 650;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
.
|
|
65
|
+
.custom-blocks-default-init-error__body {
|
|
66
66
|
margin: 8px 0 0;
|
|
67
67
|
font-size: 14px;
|
|
68
68
|
line-height: 1.5;
|
|
69
|
-
color: var(--
|
|
69
|
+
color: var(--custom-blocks-init-error-muted-text);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
.
|
|
72
|
+
.custom-blocks-default-init-error__details {
|
|
73
73
|
margin-top: 14px;
|
|
74
74
|
font-size: 12px;
|
|
75
|
-
color: var(--
|
|
75
|
+
color: var(--custom-blocks-init-error-muted-text);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
.
|
|
78
|
+
.custom-blocks-default-init-error__pre {
|
|
79
79
|
margin: 8px 0 0;
|
|
80
80
|
padding: 10px;
|
|
81
81
|
overflow: auto;
|
|
82
82
|
border-radius: 8px;
|
|
83
|
-
background: var(--
|
|
84
|
-
color: var(--
|
|
83
|
+
background: var(--custom-blocks-init-error-pre-background);
|
|
84
|
+
color: var(--custom-blocks-init-error-pre-text);
|
|
85
85
|
font-size: 12px;
|
|
86
86
|
line-height: 1.4;
|
|
87
87
|
white-space: pre-wrap;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
@media (prefers-color-scheme: dark) {
|
|
91
|
-
.
|
|
92
|
-
--
|
|
93
|
-
--
|
|
94
|
-
--
|
|
91
|
+
.custom-blocks-standalone-banner {
|
|
92
|
+
--custom-blocks-standalone-banner-background: #3b2f0b;
|
|
93
|
+
--custom-blocks-standalone-banner-border: #6f5713;
|
|
94
|
+
--custom-blocks-standalone-banner-text: #f5d36c;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
.
|
|
98
|
-
--
|
|
99
|
-
--
|
|
100
|
-
--
|
|
101
|
-
--
|
|
102
|
-
--
|
|
103
|
-
--
|
|
104
|
-
--
|
|
105
|
-
--
|
|
106
|
-
--
|
|
97
|
+
.custom-blocks-default-init-error {
|
|
98
|
+
--custom-blocks-init-error-background: #191918;
|
|
99
|
+
--custom-blocks-init-error-card-background: #20201f;
|
|
100
|
+
--custom-blocks-init-error-border: #373633;
|
|
101
|
+
--custom-blocks-init-error-text: #f1f1ef;
|
|
102
|
+
--custom-blocks-init-error-muted-text: #b9b8b3;
|
|
103
|
+
--custom-blocks-init-error-eyebrow-text: #9b9a97;
|
|
104
|
+
--custom-blocks-init-error-pre-background: #2a2a28;
|
|
105
|
+
--custom-blocks-init-error-pre-text: #e8e8e4;
|
|
106
|
+
--custom-blocks-init-error-shadow: rgba(0, 0, 0, 0.35);
|
|
107
107
|
|
|
108
108
|
color-scheme: dark;
|
|
109
109
|
}
|
|
@@ -45,7 +45,7 @@ export function NotionCustomBlock({ children, timeoutMs, fallback = null, errorF
|
|
|
45
45
|
if (!isStandalone) {
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
|
-
console.warn(`[
|
|
48
|
+
console.warn(`[custom-blocks-sdk] ${init.error?.message}`);
|
|
49
49
|
seedStandalonePreviewState();
|
|
50
50
|
}, [isStandalone, init.error]);
|
|
51
51
|
if (debugOpen) {
|
|
@@ -65,7 +65,7 @@ export function NotionCustomBlock({ children, timeoutMs, fallback = null, errorF
|
|
|
65
65
|
if (host.status !== "initialized") {
|
|
66
66
|
return _jsx(_Fragment, { children: fallback });
|
|
67
67
|
}
|
|
68
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { role: "status", className: "
|
|
68
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { role: "status", className: "custom-blocks-standalone-banner", children: "Notion host not detected \u2014 running in standalone preview. SDK hooks return placeholder values until embedded in Notion." }), children] }));
|
|
69
69
|
}
|
|
70
70
|
if (!init.isLoaded) {
|
|
71
71
|
return _jsx(_Fragment, { children: fallback });
|
|
@@ -75,7 +75,7 @@ export function NotionCustomBlock({ children, timeoutMs, fallback = null, errorF
|
|
|
75
75
|
// TODO(custom-blocks): Align the styling of this with the host-side error states.
|
|
76
76
|
function DefaultInitErrorFallback({ error }) {
|
|
77
77
|
useEffect(() => {
|
|
78
|
-
console.warn(`[
|
|
78
|
+
console.warn(`[custom-blocks-sdk] Custom block init failed: ${error.message}`);
|
|
79
79
|
}, [error]);
|
|
80
|
-
return (_jsx("div", { role: "alert", className: "
|
|
80
|
+
return (_jsx("div", { role: "alert", className: "custom-blocks-default-init-error", children: _jsxs("div", { className: "custom-blocks-default-init-error__card", children: [_jsx("div", { className: "custom-blocks-default-init-error__eyebrow", children: "Custom block" }), _jsx("h2", { className: "custom-blocks-default-init-error__title", children: "Couldn't connect to Notion" }), _jsx("p", { className: "custom-blocks-default-init-error__body", children: "This custom block loaded, but the setup handshake didn't finish. Try again from Notion. If this keeps happening, check the browser console for SDK details." }), _jsxs("details", { className: "custom-blocks-default-init-error__details", children: [_jsx("summary", { children: "Developer details" }), _jsx("pre", { className: "custom-blocks-default-init-error__pre", children: error.message })] })] }) }));
|
|
81
81
|
}
|
package/dist/utils.js
CHANGED
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
* are covered when using discriminated unions.
|
|
7
7
|
*/
|
|
8
8
|
export function unreachable(value) {
|
|
9
|
-
throw new Error(`[
|
|
9
|
+
throw new Error(`[custom-blocks-sdk] Unexpected value encountered: ${JSON.stringify(value)}`);
|
|
10
10
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* This checked-in value intentionally stays generic for local development. The SDK publish build
|
|
6
6
|
* overwrites the compiled runtime module with package.json's semver before publishing.
|
|
7
7
|
*/
|
|
8
|
-
export declare const
|
|
8
|
+
export declare const CUSTOM_BLOCKS_SDK_VERSION: string;
|
|
9
9
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/version.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../home/runner/work/custom-blocks/custom-blocks/sdk/src/version.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAgB,CAAA"}
|
package/dist/version.js
CHANGED
package/docs/block-location.md
CHANGED
|
@@ -46,7 +46,7 @@ For non-React renderers, use `customBlock.getParent()` after `initCustomBlock()`
|
|
|
46
46
|
To identify custom blocks in agent-owned instruction content:
|
|
47
47
|
|
|
48
48
|
```tsx
|
|
49
|
-
import { useParent } from "
|
|
49
|
+
import { useParent } from "@notionhq/custom-blocks"
|
|
50
50
|
|
|
51
51
|
export function AgentInstructionBadge() {
|
|
52
52
|
const parent = useParent()
|
package/docs/manifest.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Manifest
|
|
2
2
|
|
|
3
|
-
A custom
|
|
3
|
+
A custom block declares its required data sources in `custom_blocks.json` at the project root. Notion uses the manifest to know what semantic keys the block expects, what shape each property should be, and what to show when an admin is configuring the block.
|
|
4
4
|
|
|
5
5
|
```json
|
|
6
6
|
{
|
package/docs/pages.md
CHANGED
|
@@ -53,7 +53,7 @@ type CreatePageParent =
|
|
|
53
53
|
| { type: "data_source_key"; key: string };
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
`type: "data_source_key"` is the recommended form inside a custom
|
|
56
|
+
`type: "data_source_key"` is the recommended form inside a custom block. Pass the semantic key you declared in `custom_blocks.json` (e.g. `"tasks"`) and the SDK looks up the corresponding data source for you. The other two variants exist for the rarer case where you already have a raw Notion ID in hand.
|
|
57
57
|
|
|
58
58
|
### Property keys
|
|
59
59
|
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
UseDataSourceOptions,
|
|
14
14
|
} from "../types.js"
|
|
15
15
|
import { unreachable } from "../utils.js"
|
|
16
|
-
import {
|
|
16
|
+
import { CUSTOM_BLOCKS_SDK_VERSION } from "../version.js"
|
|
17
17
|
import type {
|
|
18
18
|
NotionDataSource,
|
|
19
19
|
NotionDataSourceBindings,
|
|
@@ -167,7 +167,7 @@ export class SandboxBridge {
|
|
|
167
167
|
return
|
|
168
168
|
}
|
|
169
169
|
if (this.hasSentReady) {
|
|
170
|
-
console.warn("[
|
|
170
|
+
console.warn("[custom-blocks-sdk] ignoring duplicate ready message")
|
|
171
171
|
return
|
|
172
172
|
}
|
|
173
173
|
const { manifest, error } = manifestResult
|
|
@@ -179,21 +179,21 @@ export class SandboxBridge {
|
|
|
179
179
|
type: "ready",
|
|
180
180
|
status: "error",
|
|
181
181
|
bridgeProtocolVersion: CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION,
|
|
182
|
-
sdkVersion:
|
|
182
|
+
sdkVersion: CUSTOM_BLOCKS_SDK_VERSION,
|
|
183
183
|
error,
|
|
184
184
|
}
|
|
185
185
|
: {
|
|
186
186
|
type: "ready",
|
|
187
187
|
status: "success",
|
|
188
188
|
bridgeProtocolVersion: CUSTOM_BLOCK_BRIDGE_PROTOCOL_VERSION,
|
|
189
|
-
sdkVersion:
|
|
189
|
+
sdkVersion: CUSTOM_BLOCKS_SDK_VERSION,
|
|
190
190
|
manifest,
|
|
191
191
|
}
|
|
192
192
|
this.postToHost(readyMessage)
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
private postToHost(message: unknown) {
|
|
196
|
-
console.debug("[
|
|
196
|
+
console.debug("[custom-blocks-sdk] outbound postMessage", message)
|
|
197
197
|
this.logMessage("sent", message)
|
|
198
198
|
window.parent.postMessage(message, "*")
|
|
199
199
|
}
|
|
@@ -205,7 +205,7 @@ export class SandboxBridge {
|
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
private handleMessage = (event: MessageEvent) => {
|
|
208
|
-
console.debug("[
|
|
208
|
+
console.debug("[custom-blocks-sdk] incoming postMessage", {
|
|
209
209
|
data: event.data,
|
|
210
210
|
fromParent: event.source === window.parent,
|
|
211
211
|
})
|
|
@@ -217,7 +217,7 @@ export class SandboxBridge {
|
|
|
217
217
|
const parsed = v.safeParse(hostToSandboxMessageSchema, event.data)
|
|
218
218
|
if (!parsed.success) {
|
|
219
219
|
console.warn(
|
|
220
|
-
"[
|
|
220
|
+
"[custom-blocks-sdk] ignoring malformed host message",
|
|
221
221
|
parsed.issues,
|
|
222
222
|
)
|
|
223
223
|
const incomingType = readIncomingType(event.data)
|
|
@@ -245,7 +245,7 @@ export class SandboxBridge {
|
|
|
245
245
|
// failure on its own.
|
|
246
246
|
if (message.type === "invalidSandboxMessage") {
|
|
247
247
|
console.warn(
|
|
248
|
-
"[
|
|
248
|
+
"[custom-blocks-sdk] host reported invalid sandbox message:",
|
|
249
249
|
message.reason,
|
|
250
250
|
)
|
|
251
251
|
return
|
|
@@ -263,7 +263,7 @@ export class SandboxBridge {
|
|
|
263
263
|
const hostState = this.hostState
|
|
264
264
|
|
|
265
265
|
if (hostState.status !== "initialized") {
|
|
266
|
-
console.warn(`[
|
|
266
|
+
console.warn(`[custom-blocks-sdk] ignoring ${message.type} before init`)
|
|
267
267
|
return
|
|
268
268
|
}
|
|
269
269
|
|
|
@@ -340,7 +340,7 @@ export class SandboxBridge {
|
|
|
340
340
|
: { status: "error", error: message.error }
|
|
341
341
|
if (!this.pendingCreatePage.resolve(message.requestId, result)) {
|
|
342
342
|
console.warn(
|
|
343
|
-
`[
|
|
343
|
+
`[custom-blocks-sdk] createPageResult for unknown requestId ${message.requestId}`,
|
|
344
344
|
)
|
|
345
345
|
}
|
|
346
346
|
return
|
|
@@ -353,7 +353,7 @@ export class SandboxBridge {
|
|
|
353
353
|
: { status: "error", error: message.error }
|
|
354
354
|
if (!this.pendingGetPage.resolve(message.requestId, result)) {
|
|
355
355
|
console.warn(
|
|
356
|
-
`[
|
|
356
|
+
`[custom-blocks-sdk] getPageResult for unknown requestId ${message.requestId}`,
|
|
357
357
|
)
|
|
358
358
|
}
|
|
359
359
|
return
|
|
@@ -366,7 +366,7 @@ export class SandboxBridge {
|
|
|
366
366
|
: { status: "error", error: message.error }
|
|
367
367
|
if (!this.pendingGetUser.resolve(message.requestId, result)) {
|
|
368
368
|
console.warn(
|
|
369
|
-
`[
|
|
369
|
+
`[custom-blocks-sdk] getUserResult for unknown requestId ${message.requestId}`,
|
|
370
370
|
)
|
|
371
371
|
}
|
|
372
372
|
return
|
|
@@ -379,7 +379,7 @@ export class SandboxBridge {
|
|
|
379
379
|
: { status: "error", error: message.error }
|
|
380
380
|
if (!this.pendingListUsers.resolve(message.requestId, result)) {
|
|
381
381
|
console.warn(
|
|
382
|
-
`[
|
|
382
|
+
`[custom-blocks-sdk] listUsersResult for unknown requestId ${message.requestId}`,
|
|
383
383
|
)
|
|
384
384
|
}
|
|
385
385
|
return
|
|
@@ -392,7 +392,7 @@ export class SandboxBridge {
|
|
|
392
392
|
: { status: "error", error: message.error }
|
|
393
393
|
if (!this.pendingUpdatePage.resolve(message.requestId, result)) {
|
|
394
394
|
console.warn(
|
|
395
|
-
`[
|
|
395
|
+
`[custom-blocks-sdk] updatePageResult for unknown requestId ${message.requestId}`,
|
|
396
396
|
)
|
|
397
397
|
}
|
|
398
398
|
return
|
|
@@ -492,7 +492,7 @@ export class SandboxBridge {
|
|
|
492
492
|
// the browser console either way. We deliberately don't NACK the host or post anything
|
|
493
493
|
// back over the bridge since the host has already given up.
|
|
494
494
|
console.error(
|
|
495
|
-
`[
|
|
495
|
+
`[custom-blocks-sdk] host reported init error (${message.error.code}): ${message.error.message}`,
|
|
496
496
|
)
|
|
497
497
|
if (this.rejectInit) {
|
|
498
498
|
this.rejectInit(new CustomBlockInitError(message.error))
|
|
@@ -900,13 +900,13 @@ function resolveDataSourceQueryLimit(limit: number | undefined): number {
|
|
|
900
900
|
}
|
|
901
901
|
if (!Number.isFinite(limit) || !Number.isInteger(limit) || limit < 1) {
|
|
902
902
|
console.warn(
|
|
903
|
-
`[
|
|
903
|
+
`[custom-blocks-sdk] useDataSource limit must be a positive integer; using ${DEFAULT_DATA_SOURCE_QUERY_LIMIT}.`,
|
|
904
904
|
)
|
|
905
905
|
return DEFAULT_DATA_SOURCE_QUERY_LIMIT
|
|
906
906
|
}
|
|
907
907
|
if (limit > MAX_DATA_SOURCE_QUERY_LIMIT) {
|
|
908
908
|
console.warn(
|
|
909
|
-
`[
|
|
909
|
+
`[custom-blocks-sdk] useDataSource limit is capped at ${MAX_DATA_SOURCE_QUERY_LIMIT}.`,
|
|
910
910
|
)
|
|
911
911
|
return MAX_DATA_SOURCE_QUERY_LIMIT
|
|
912
912
|
}
|
|
@@ -41,7 +41,7 @@ export type ManifestLoadResult =
|
|
|
41
41
|
export async function loadManifest(): Promise<ManifestLoadResult> {
|
|
42
42
|
if (typeof fetch !== "function") {
|
|
43
43
|
const message = `No fetch API available; cannot load ${MANIFEST_URL}.`
|
|
44
|
-
console.warn(`[
|
|
44
|
+
console.warn(`[custom-blocks-sdk] ${message}`)
|
|
45
45
|
return {
|
|
46
46
|
manifest: null,
|
|
47
47
|
error: { code: "manifest_unavailable", message },
|
|
@@ -52,7 +52,7 @@ export async function loadManifest(): Promise<ManifestLoadResult> {
|
|
|
52
52
|
response = await fetch(MANIFEST_URL, { credentials: "omit" })
|
|
53
53
|
} catch (error) {
|
|
54
54
|
const message = `Could not fetch ${MANIFEST_URL}.`
|
|
55
|
-
console.warn(`[
|
|
55
|
+
console.warn(`[custom-blocks-sdk] ${message}`, error)
|
|
56
56
|
return {
|
|
57
57
|
manifest: null,
|
|
58
58
|
error: { code: "manifest_unavailable", message },
|
|
@@ -60,12 +60,12 @@ export async function loadManifest(): Promise<ManifestLoadResult> {
|
|
|
60
60
|
}
|
|
61
61
|
if (response.status === 404) {
|
|
62
62
|
const message = `No manifest found at ${MANIFEST_URL} (status ${response.status}).`
|
|
63
|
-
console.warn(`[
|
|
63
|
+
console.warn(`[custom-blocks-sdk] ${message}`)
|
|
64
64
|
return { manifest: null }
|
|
65
65
|
}
|
|
66
66
|
if (!response.ok) {
|
|
67
67
|
const message = `Could not fetch ${MANIFEST_URL} (status ${response.status}).`
|
|
68
|
-
console.warn(`[
|
|
68
|
+
console.warn(`[custom-blocks-sdk] ${message}`)
|
|
69
69
|
return {
|
|
70
70
|
manifest: null,
|
|
71
71
|
error: { code: "manifest_unavailable", message },
|
|
@@ -76,13 +76,13 @@ export async function loadManifest(): Promise<ManifestLoadResult> {
|
|
|
76
76
|
json = await response.json()
|
|
77
77
|
} catch (error) {
|
|
78
78
|
const message = `Manifest at ${MANIFEST_URL} was not valid JSON.`
|
|
79
|
-
console.warn(`[
|
|
79
|
+
console.warn(`[custom-blocks-sdk] ${message}`, error)
|
|
80
80
|
return { manifest: null, error: { code: "manifest_invalid", message } }
|
|
81
81
|
}
|
|
82
82
|
const parsed = v.safeParse(manifestSchema, json)
|
|
83
83
|
if (!parsed.success) {
|
|
84
84
|
const message = `Manifest at ${MANIFEST_URL} did not match schema.`
|
|
85
|
-
console.warn(`[
|
|
85
|
+
console.warn(`[custom-blocks-sdk] ${message}`, parsed.issues)
|
|
86
86
|
return { manifest: null, error: { code: "manifest_invalid", message } }
|
|
87
87
|
}
|
|
88
88
|
return { manifest: parsed.output }
|
package/src/init.ts
CHANGED
|
@@ -106,7 +106,7 @@ export function initCustomBlock(
|
|
|
106
106
|
}
|
|
107
107
|
if (message.status === "error") {
|
|
108
108
|
console.error(
|
|
109
|
-
`[
|
|
109
|
+
`[custom-blocks-sdk] host reported init error (${message.error.code}): ${message.error.message}`,
|
|
110
110
|
)
|
|
111
111
|
throw new CustomBlockInitError(message.error)
|
|
112
112
|
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
.
|
|
2
|
-
--
|
|
3
|
-
--
|
|
4
|
-
--
|
|
1
|
+
.custom-blocks-standalone-banner {
|
|
2
|
+
--custom-blocks-standalone-banner-background: #fff8e1;
|
|
3
|
+
--custom-blocks-standalone-banner-border: #f0d77b;
|
|
4
|
+
--custom-blocks-standalone-banner-text: #5d4200;
|
|
5
5
|
|
|
6
6
|
padding: 8px 12px;
|
|
7
|
-
background: var(--
|
|
8
|
-
color: var(--
|
|
9
|
-
border-bottom: 1px solid var(--
|
|
7
|
+
background: var(--custom-blocks-standalone-banner-background);
|
|
8
|
+
color: var(--custom-blocks-standalone-banner-text);
|
|
9
|
+
border-bottom: 1px solid var(--custom-blocks-standalone-banner-border);
|
|
10
10
|
font-size: 13px;
|
|
11
11
|
font-family: system-ui, sans-serif;
|
|
12
12
|
line-height: 1.4;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
.
|
|
16
|
-
--
|
|
17
|
-
--
|
|
18
|
-
--
|
|
19
|
-
--
|
|
20
|
-
--
|
|
21
|
-
--
|
|
22
|
-
--
|
|
23
|
-
--
|
|
24
|
-
--
|
|
15
|
+
.custom-blocks-default-init-error {
|
|
16
|
+
--custom-blocks-init-error-background: #f7f7f5;
|
|
17
|
+
--custom-blocks-init-error-card-background: #ffffff;
|
|
18
|
+
--custom-blocks-init-error-border: #e5e5e1;
|
|
19
|
+
--custom-blocks-init-error-text: #1f1f1d;
|
|
20
|
+
--custom-blocks-init-error-muted-text: #5f5e5b;
|
|
21
|
+
--custom-blocks-init-error-eyebrow-text: #787774;
|
|
22
|
+
--custom-blocks-init-error-pre-background: #f1f1ef;
|
|
23
|
+
--custom-blocks-init-error-pre-text: #37352f;
|
|
24
|
+
--custom-blocks-init-error-shadow: rgba(0, 0, 0, 0.08);
|
|
25
25
|
|
|
26
26
|
box-sizing: border-box;
|
|
27
27
|
min-height: 240px;
|
|
@@ -29,81 +29,81 @@
|
|
|
29
29
|
align-items: center;
|
|
30
30
|
justify-content: center;
|
|
31
31
|
padding: 24px;
|
|
32
|
-
background: var(--
|
|
33
|
-
color: var(--
|
|
32
|
+
background: var(--custom-blocks-init-error-background);
|
|
33
|
+
color: var(--custom-blocks-init-error-text);
|
|
34
34
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
|
35
35
|
color-scheme: light;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
.
|
|
38
|
+
.custom-blocks-default-init-error__card {
|
|
39
39
|
box-sizing: border-box;
|
|
40
40
|
width: 100%;
|
|
41
41
|
max-width: 420px;
|
|
42
42
|
padding: 20px;
|
|
43
|
-
border: 1px solid var(--
|
|
43
|
+
border: 1px solid var(--custom-blocks-init-error-border);
|
|
44
44
|
border-radius: 12px;
|
|
45
|
-
background: var(--
|
|
46
|
-
box-shadow: 0 8px 24px var(--
|
|
45
|
+
background: var(--custom-blocks-init-error-card-background);
|
|
46
|
+
box-shadow: 0 8px 24px var(--custom-blocks-init-error-shadow);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
.
|
|
49
|
+
.custom-blocks-default-init-error__eyebrow {
|
|
50
50
|
margin-bottom: 8px;
|
|
51
51
|
font-size: 11px;
|
|
52
52
|
font-weight: 600;
|
|
53
53
|
letter-spacing: 0.08em;
|
|
54
54
|
text-transform: uppercase;
|
|
55
|
-
color: var(--
|
|
55
|
+
color: var(--custom-blocks-init-error-eyebrow-text);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
.
|
|
58
|
+
.custom-blocks-default-init-error__title {
|
|
59
59
|
margin: 0;
|
|
60
60
|
font-size: 18px;
|
|
61
61
|
line-height: 1.3;
|
|
62
62
|
font-weight: 650;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
.
|
|
65
|
+
.custom-blocks-default-init-error__body {
|
|
66
66
|
margin: 8px 0 0;
|
|
67
67
|
font-size: 14px;
|
|
68
68
|
line-height: 1.5;
|
|
69
|
-
color: var(--
|
|
69
|
+
color: var(--custom-blocks-init-error-muted-text);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
.
|
|
72
|
+
.custom-blocks-default-init-error__details {
|
|
73
73
|
margin-top: 14px;
|
|
74
74
|
font-size: 12px;
|
|
75
|
-
color: var(--
|
|
75
|
+
color: var(--custom-blocks-init-error-muted-text);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
.
|
|
78
|
+
.custom-blocks-default-init-error__pre {
|
|
79
79
|
margin: 8px 0 0;
|
|
80
80
|
padding: 10px;
|
|
81
81
|
overflow: auto;
|
|
82
82
|
border-radius: 8px;
|
|
83
|
-
background: var(--
|
|
84
|
-
color: var(--
|
|
83
|
+
background: var(--custom-blocks-init-error-pre-background);
|
|
84
|
+
color: var(--custom-blocks-init-error-pre-text);
|
|
85
85
|
font-size: 12px;
|
|
86
86
|
line-height: 1.4;
|
|
87
87
|
white-space: pre-wrap;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
@media (prefers-color-scheme: dark) {
|
|
91
|
-
.
|
|
92
|
-
--
|
|
93
|
-
--
|
|
94
|
-
--
|
|
91
|
+
.custom-blocks-standalone-banner {
|
|
92
|
+
--custom-blocks-standalone-banner-background: #3b2f0b;
|
|
93
|
+
--custom-blocks-standalone-banner-border: #6f5713;
|
|
94
|
+
--custom-blocks-standalone-banner-text: #f5d36c;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
.
|
|
98
|
-
--
|
|
99
|
-
--
|
|
100
|
-
--
|
|
101
|
-
--
|
|
102
|
-
--
|
|
103
|
-
--
|
|
104
|
-
--
|
|
105
|
-
--
|
|
106
|
-
--
|
|
97
|
+
.custom-blocks-default-init-error {
|
|
98
|
+
--custom-blocks-init-error-background: #191918;
|
|
99
|
+
--custom-blocks-init-error-card-background: #20201f;
|
|
100
|
+
--custom-blocks-init-error-border: #373633;
|
|
101
|
+
--custom-blocks-init-error-text: #f1f1ef;
|
|
102
|
+
--custom-blocks-init-error-muted-text: #b9b8b3;
|
|
103
|
+
--custom-blocks-init-error-eyebrow-text: #9b9a97;
|
|
104
|
+
--custom-blocks-init-error-pre-background: #2a2a28;
|
|
105
|
+
--custom-blocks-init-error-pre-text: #e8e8e4;
|
|
106
|
+
--custom-blocks-init-error-shadow: rgba(0, 0, 0, 0.35);
|
|
107
107
|
|
|
108
108
|
color-scheme: dark;
|
|
109
109
|
}
|
|
@@ -80,7 +80,7 @@ export function NotionCustomBlock({
|
|
|
80
80
|
if (!isStandalone) {
|
|
81
81
|
return
|
|
82
82
|
}
|
|
83
|
-
console.warn(`[
|
|
83
|
+
console.warn(`[custom-blocks-sdk] ${init.error?.message}`)
|
|
84
84
|
seedStandalonePreviewState()
|
|
85
85
|
}, [isStandalone, init.error])
|
|
86
86
|
|
|
@@ -107,7 +107,7 @@ export function NotionCustomBlock({
|
|
|
107
107
|
}
|
|
108
108
|
return (
|
|
109
109
|
<>
|
|
110
|
-
<div role="status" className="
|
|
110
|
+
<div role="status" className="custom-blocks-standalone-banner">
|
|
111
111
|
Notion host not detected — running in standalone preview. SDK hooks
|
|
112
112
|
return placeholder values until embedded in Notion.
|
|
113
113
|
</div>
|
|
@@ -125,25 +125,29 @@ export function NotionCustomBlock({
|
|
|
125
125
|
function DefaultInitErrorFallback({ error }: { error: Error }) {
|
|
126
126
|
useEffect(() => {
|
|
127
127
|
console.warn(
|
|
128
|
-
`[
|
|
128
|
+
`[custom-blocks-sdk] Custom block init failed: ${error.message}`,
|
|
129
129
|
)
|
|
130
130
|
}, [error])
|
|
131
131
|
|
|
132
132
|
return (
|
|
133
|
-
<div role="alert" className="
|
|
134
|
-
<div className="
|
|
135
|
-
<div className="
|
|
136
|
-
|
|
133
|
+
<div role="alert" className="custom-blocks-default-init-error">
|
|
134
|
+
<div className="custom-blocks-default-init-error__card">
|
|
135
|
+
<div className="custom-blocks-default-init-error__eyebrow">
|
|
136
|
+
Custom block
|
|
137
|
+
</div>
|
|
138
|
+
<h2 className="custom-blocks-default-init-error__title">
|
|
137
139
|
Couldn't connect to Notion
|
|
138
140
|
</h2>
|
|
139
|
-
<p className="
|
|
141
|
+
<p className="custom-blocks-default-init-error__body">
|
|
140
142
|
This custom block loaded, but the setup handshake didn't finish.
|
|
141
143
|
Try again from Notion. If this keeps happening, check the browser
|
|
142
144
|
console for SDK details.
|
|
143
145
|
</p>
|
|
144
|
-
<details className="
|
|
146
|
+
<details className="custom-blocks-default-init-error__details">
|
|
145
147
|
<summary>Developer details</summary>
|
|
146
|
-
<pre className="
|
|
148
|
+
<pre className="custom-blocks-default-init-error__pre">
|
|
149
|
+
{error.message}
|
|
150
|
+
</pre>
|
|
147
151
|
</details>
|
|
148
152
|
</div>
|
|
149
153
|
</div>
|
package/src/utils.ts
CHANGED
package/src/version.ts
CHANGED
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
* This checked-in value intentionally stays generic for local development. The SDK publish build
|
|
6
6
|
* overwrites the compiled runtime module with package.json's semver before publishing.
|
|
7
7
|
*/
|
|
8
|
-
export const
|
|
8
|
+
export const CUSTOM_BLOCKS_SDK_VERSION: string = "0.0.0"
|