@wix/himalaya-cli 0.1079.0 → 0.1080.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/builder.d.ts +2 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +41 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/site-shell.d.ts +0 -14
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/validator.d.ts +36 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/rich-content/src/display.d.ts +26 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/rich-content/src/index.d.ts +79 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +23 -49
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdui.mjs +11 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-rich-content.mjs +344 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/package.json +4 -0
- package/dist/cli.mjs +12 -4
- package/guides/importable-sdks.md +1 -0
- package/guides/sdk-reference.json +74 -1
- package/guides/splash-screens.md +13 -1
- package/package.json +2 -2
- package/templates/beauty-salon-owner/tier5-src/_shared/owner-post-content.ts +7 -26
- package/templates/beauty-salon-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
- package/templates/community-club-owner/tier5-src/_shared/owner-post-content.ts +7 -26
- package/templates/community-club-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
- package/templates/community-club-owner/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
- package/templates/events-meetups-owner/tier5-src/_shared/owner-post-content.ts +7 -26
- package/templates/events-meetups-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
- package/templates/gym-fitness-owner/tier5-src/_shared/owner-post-content.ts +7 -26
- package/templates/gym-fitness-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
- package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-post-content.ts +7 -26
- package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
- package/templates/owner-lite/tier5-src/_shared/owner-post-content.ts +7 -26
- package/templates/owner-lite/tier5-src/_shared/owner-rich-guard.ts +12 -41
- package/templates/owner-lite/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
- package/templates/podcast-creator-owner/tier5-src/_shared/owner-post-content.ts +7 -26
- package/templates/podcast-creator-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
- package/templates/portfolio-creative-owner/tier5-src/_shared/owner-post-content.ts +7 -26
- package/templates/portfolio-creative-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
- package/templates/restaurant-owner/tier5-src/_shared/owner-post-content.ts +7 -26
- package/templates/restaurant-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
- package/templates/retail-store-owner/tier5-src/_shared/owner-post-content.ts +7 -26
- package/templates/retail-store-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
- package/templates/yoga-studio-owner/tier5-src/_shared/owner-post-content.ts +7 -26
- package/templates/yoga-studio-owner/tier5-src/_shared/owner-rich-guard.ts +12 -41
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event-form/index.ts +2 -2
|
@@ -1,35 +1,16 @@
|
|
|
1
1
|
import type { WixRichContent } from "@himalaya/wix-blog";
|
|
2
|
+
// The rich-content helpers. Inside the platform source this import is a relative path, so node test runs
|
|
3
|
+
// resolve it; an app's copy imports "@himalaya/wix-rich-content".
|
|
4
|
+
import { ricosToPlainText, isPlainRicos, plainTextToRicos } from "@himalaya/wix-rich-content";
|
|
2
5
|
import { wixMediaUrl, WIX_DETAIL_IMAGE } from "./himi-store.js";
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
// Thin wrappers keep the WixRichContent signatures callers compile against.
|
|
5
8
|
/** Join inline runs without adding paragraph breaks between bold/plain spans. */
|
|
6
|
-
export
|
|
7
|
-
const inline = (node: Node): string => typeof node.textData?.text === "string"
|
|
8
|
-
? node.textData.text : (node.nodes ?? []).map(inline).join("");
|
|
9
|
-
const blocks = (nodes: Node[]): string[] => nodes.flatMap(node =>
|
|
10
|
-
node.type === "PARAGRAPH" || node.type === "HEADING" || node.type === "TEXT"
|
|
11
|
-
? [inline(node)] : blocks(node.nodes ?? []));
|
|
12
|
-
return blocks((rich?.nodes ?? []) as Node[]).join("\n\n");
|
|
13
|
-
}
|
|
9
|
+
export const postBodyText = (rich: WixRichContent | null | undefined): string => ricosToPlainText(rich);
|
|
14
10
|
/** The plain editor must not overwrite formatting or any opaque media node.
|
|
15
11
|
* Rich drafts remain editable for title/excerpt/cover; body stays untouched. */
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
if (!raw || typeof raw !== "object") return true;
|
|
19
|
-
const node = raw as Node;
|
|
20
|
-
if (node.type !== "PARAGRAPH") return true;
|
|
21
|
-
if (Object.keys(node).some(key => !["type", "id", "nodes", "paragraphData"].includes(key))) return true;
|
|
22
|
-
if (node.paragraphData && Object.keys(node.paragraphData as object).length) return true;
|
|
23
|
-
return (node.nodes ?? []).some(child => child.type !== "TEXT"
|
|
24
|
-
|| !!child.textData?.decorations?.length
|
|
25
|
-
|| Object.keys(child).some(key => !["type", "id", "textData", "nodes"].includes(key)));
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
export function plainPostBody(body: string): WixRichContent {
|
|
29
|
-
return { nodes: body.split("\n\n").map(text => ({
|
|
30
|
-
type: "PARAGRAPH", nodes: [{ type: "TEXT", textData: { text, decorations: [] } }],
|
|
31
|
-
})) };
|
|
32
|
-
}
|
|
12
|
+
export const hasRichBody = (rich: WixRichContent | null | undefined): boolean => !isPlainRicos(rich);
|
|
13
|
+
export const plainPostBody = (body: string): WixRichContent => plainTextToRicos(body);
|
|
33
14
|
/** Current Blog media shape plus older coverImage projections. */
|
|
34
15
|
export function postCoverUrl(raw: Record<string, any> | null | undefined): string {
|
|
35
16
|
if (!raw) return "";
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// May a plain owner form round-trip this body? The Ricos and HTML checks are @himalaya/wix-rich-content's;
|
|
2
|
+
// this file adds the read-only previews the forms show for a locked body, and the FAQ's legacy DraftJS answer.
|
|
3
|
+
// Inside the platform source this import is a relative path, so node test runs resolve it; an app's copy
|
|
4
|
+
// imports "@himalaya/wix-rich-content".
|
|
5
|
+
import { htmlIsTrivial, isPlainRicos } from "@himalaya/wix-rich-content";
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
const TAG = /<[a-z!/?]/i;
|
|
7
|
-
|
|
8
|
-
/** True when a plain text field can hold this HTML without losing anything: empty, bare text
|
|
9
|
-
* (no tag — a lone `<` in prose is text), or exactly one attribute-less `<p>…</p>` with no
|
|
10
|
-
* inner tags. */
|
|
11
|
-
export function htmlIsTrivial(html: string | null | undefined): boolean {
|
|
12
|
-
const s = (html ?? "").trim();
|
|
13
|
-
return !s || !TAG.test(s) || /^<p>[^<>]*<\/p>$/.test(s);
|
|
14
|
-
}
|
|
7
|
+
export { htmlIsTrivial };
|
|
15
8
|
|
|
16
9
|
const ENTITIES: Record<string, string> = { " ": " ", "&": "&", "<": "<", ">": ">", """: "\"", "'": "'" };
|
|
17
10
|
|
|
@@ -23,35 +16,13 @@ export function htmlPreviewText(html: string | null | undefined): string {
|
|
|
23
16
|
.split("\n").map((l) => l.replace(/\s+/g, " ").trim()).filter(Boolean).join("\n");
|
|
24
17
|
}
|
|
25
18
|
|
|
26
|
-
type RicosNode = { type?: unknown; nodes?: unknown; textData?: { decorations?: unknown }; paragraphData?: unknown; [key: string]: unknown };
|
|
27
|
-
const onlyKeys = (o: object, allowed: string[]) => Object.keys(o).every((k) => allowed.includes(k));
|
|
28
|
-
const emptyObject = (v: unknown) => v === undefined || (!!v && typeof v === "object" && Object.keys(v as object).length === 0);
|
|
29
|
-
|
|
30
|
-
/** The paragraph styling a PLAIN paragraph still carries on the Ricos wire (Events v3 sends
|
|
31
|
-
* `{ textStyle: { textAlignment: "AUTO" }, indentation: 0 }` on every paragraph). */
|
|
32
|
-
function defaultParagraphData(pd: unknown): boolean {
|
|
33
|
-
if (pd === undefined) return true;
|
|
34
|
-
if (!pd || typeof pd !== "object" || !onlyKeys(pd, ["textStyle", "indentation"])) return false;
|
|
35
|
-
const { textStyle, indentation } = pd as { textStyle?: unknown; indentation?: unknown };
|
|
36
|
-
if (indentation !== undefined && indentation !== 0) return false;
|
|
37
|
-
if (textStyle === undefined) return true;
|
|
38
|
-
if (!textStyle || typeof textStyle !== "object" || !onlyKeys(textStyle, ["textAlignment"])) return false;
|
|
39
|
-
const align = (textStyle as { textAlignment?: unknown }).textAlignment;
|
|
40
|
-
return align === undefined || align === "AUTO";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
19
|
/** True when a plain text field can round-trip this Ricos document: no document styling, and
|
|
44
|
-
* only paragraphs of undecorated text whose paragraph data is the wire default
|
|
45
|
-
*
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
&& onlyKeys(n, ["type", "id", "nodes", "paragraphData"]) && defaultParagraphData(n.paragraphData)
|
|
51
|
-
&& (Array.isArray(n.nodes) ? (n.nodes as RicosNode[]) : []).every((t) => !!t && typeof t === "object" && t.type === "TEXT"
|
|
52
|
-
&& onlyKeys(t, ["type", "id", "textData", "nodes"]) && (!Array.isArray(t.nodes) || t.nodes.length === 0)
|
|
53
|
-
&& !(Array.isArray(t.textData?.decorations) && t.textData.decorations.length)));
|
|
54
|
-
}
|
|
20
|
+
* only paragraphs of undecorated text whose paragraph data is the wire default (Events v3 sends
|
|
21
|
+
* `{ textStyle: { textAlignment: "AUTO" }, indentation: 0 }` on every paragraph). Anything else —
|
|
22
|
+
* a heading, list, link, bold run, media node, alignment or indent — is rich, and so is a missing
|
|
23
|
+
* document (callers decide what an absent body means). */
|
|
24
|
+
export const ricosIsPlain = (doc: { nodes?: unknown; documentStyle?: unknown } | null | undefined): boolean =>
|
|
25
|
+
!!doc && isPlainRicos(doc);
|
|
55
26
|
|
|
56
27
|
/** Preview text for a legacy DraftJS document: its `blocks[].text` joined by a blank line, or ""
|
|
57
28
|
* when the payload is not DraftJS JSON (never a throw). */
|
|
@@ -10,8 +10,8 @@ import { defineService } from "@himalaya/state";
|
|
|
10
10
|
import { getEvent, updateEvent, type WixEvent } from "@himalaya/wix-events";
|
|
11
11
|
import { normalizeEvent } from "../_shared/himi-events.js";
|
|
12
12
|
import { postBodyText, plainPostBody } from "../_shared/owner-post-content.js";
|
|
13
|
-
//
|
|
14
|
-
//
|
|
13
|
+
// Events v3 sends default paragraphData on EVERY paragraph and an empty documentStyle; ricosIsPlain
|
|
14
|
+
// reads both as plain.
|
|
15
15
|
import { ricosIsPlain } from "../_shared/owner-rich-guard.js";
|
|
16
16
|
import { ownerLoadError, isBridgeUnavailable, isOwnerPreviewDemo, shouldQueueOwnerWrite } from "../_shared/owner-load-error.js";
|
|
17
17
|
import { DEMO_EVENTS, DEMO_NOTE, DEMO_SAVE_NOTE, demoRefusalPatch } from "../_shared/owner-preview-demo.js";
|
|
@@ -1,35 +1,16 @@
|
|
|
1
1
|
import type { WixRichContent } from "@himalaya/wix-blog";
|
|
2
|
+
// The rich-content helpers. Inside the platform source this import is a relative path, so node test runs
|
|
3
|
+
// resolve it; an app's copy imports "@himalaya/wix-rich-content".
|
|
4
|
+
import { ricosToPlainText, isPlainRicos, plainTextToRicos } from "@himalaya/wix-rich-content";
|
|
2
5
|
import { wixMediaUrl, WIX_DETAIL_IMAGE } from "./himi-store.js";
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
// Thin wrappers keep the WixRichContent signatures callers compile against.
|
|
5
8
|
/** Join inline runs without adding paragraph breaks between bold/plain spans. */
|
|
6
|
-
export
|
|
7
|
-
const inline = (node: Node): string => typeof node.textData?.text === "string"
|
|
8
|
-
? node.textData.text : (node.nodes ?? []).map(inline).join("");
|
|
9
|
-
const blocks = (nodes: Node[]): string[] => nodes.flatMap(node =>
|
|
10
|
-
node.type === "PARAGRAPH" || node.type === "HEADING" || node.type === "TEXT"
|
|
11
|
-
? [inline(node)] : blocks(node.nodes ?? []));
|
|
12
|
-
return blocks((rich?.nodes ?? []) as Node[]).join("\n\n");
|
|
13
|
-
}
|
|
9
|
+
export const postBodyText = (rich: WixRichContent | null | undefined): string => ricosToPlainText(rich);
|
|
14
10
|
/** The plain editor must not overwrite formatting or any opaque media node.
|
|
15
11
|
* Rich drafts remain editable for title/excerpt/cover; body stays untouched. */
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
if (!raw || typeof raw !== "object") return true;
|
|
19
|
-
const node = raw as Node;
|
|
20
|
-
if (node.type !== "PARAGRAPH") return true;
|
|
21
|
-
if (Object.keys(node).some(key => !["type", "id", "nodes", "paragraphData"].includes(key))) return true;
|
|
22
|
-
if (node.paragraphData && Object.keys(node.paragraphData as object).length) return true;
|
|
23
|
-
return (node.nodes ?? []).some(child => child.type !== "TEXT"
|
|
24
|
-
|| !!child.textData?.decorations?.length
|
|
25
|
-
|| Object.keys(child).some(key => !["type", "id", "textData", "nodes"].includes(key)));
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
export function plainPostBody(body: string): WixRichContent {
|
|
29
|
-
return { nodes: body.split("\n\n").map(text => ({
|
|
30
|
-
type: "PARAGRAPH", nodes: [{ type: "TEXT", textData: { text, decorations: [] } }],
|
|
31
|
-
})) };
|
|
32
|
-
}
|
|
12
|
+
export const hasRichBody = (rich: WixRichContent | null | undefined): boolean => !isPlainRicos(rich);
|
|
13
|
+
export const plainPostBody = (body: string): WixRichContent => plainTextToRicos(body);
|
|
33
14
|
/** Current Blog media shape plus older coverImage projections. */
|
|
34
15
|
export function postCoverUrl(raw: Record<string, any> | null | undefined): string {
|
|
35
16
|
if (!raw) return "";
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// May a plain owner form round-trip this body? The Ricos and HTML checks are @himalaya/wix-rich-content's;
|
|
2
|
+
// this file adds the read-only previews the forms show for a locked body, and the FAQ's legacy DraftJS answer.
|
|
3
|
+
// Inside the platform source this import is a relative path, so node test runs resolve it; an app's copy
|
|
4
|
+
// imports "@himalaya/wix-rich-content".
|
|
5
|
+
import { htmlIsTrivial, isPlainRicos } from "@himalaya/wix-rich-content";
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
const TAG = /<[a-z!/?]/i;
|
|
7
|
-
|
|
8
|
-
/** True when a plain text field can hold this HTML without losing anything: empty, bare text
|
|
9
|
-
* (no tag — a lone `<` in prose is text), or exactly one attribute-less `<p>…</p>` with no
|
|
10
|
-
* inner tags. */
|
|
11
|
-
export function htmlIsTrivial(html: string | null | undefined): boolean {
|
|
12
|
-
const s = (html ?? "").trim();
|
|
13
|
-
return !s || !TAG.test(s) || /^<p>[^<>]*<\/p>$/.test(s);
|
|
14
|
-
}
|
|
7
|
+
export { htmlIsTrivial };
|
|
15
8
|
|
|
16
9
|
const ENTITIES: Record<string, string> = { " ": " ", "&": "&", "<": "<", ">": ">", """: "\"", "'": "'" };
|
|
17
10
|
|
|
@@ -23,35 +16,13 @@ export function htmlPreviewText(html: string | null | undefined): string {
|
|
|
23
16
|
.split("\n").map((l) => l.replace(/\s+/g, " ").trim()).filter(Boolean).join("\n");
|
|
24
17
|
}
|
|
25
18
|
|
|
26
|
-
type RicosNode = { type?: unknown; nodes?: unknown; textData?: { decorations?: unknown }; paragraphData?: unknown; [key: string]: unknown };
|
|
27
|
-
const onlyKeys = (o: object, allowed: string[]) => Object.keys(o).every((k) => allowed.includes(k));
|
|
28
|
-
const emptyObject = (v: unknown) => v === undefined || (!!v && typeof v === "object" && Object.keys(v as object).length === 0);
|
|
29
|
-
|
|
30
|
-
/** The paragraph styling a PLAIN paragraph still carries on the Ricos wire (Events v3 sends
|
|
31
|
-
* `{ textStyle: { textAlignment: "AUTO" }, indentation: 0 }` on every paragraph). */
|
|
32
|
-
function defaultParagraphData(pd: unknown): boolean {
|
|
33
|
-
if (pd === undefined) return true;
|
|
34
|
-
if (!pd || typeof pd !== "object" || !onlyKeys(pd, ["textStyle", "indentation"])) return false;
|
|
35
|
-
const { textStyle, indentation } = pd as { textStyle?: unknown; indentation?: unknown };
|
|
36
|
-
if (indentation !== undefined && indentation !== 0) return false;
|
|
37
|
-
if (textStyle === undefined) return true;
|
|
38
|
-
if (!textStyle || typeof textStyle !== "object" || !onlyKeys(textStyle, ["textAlignment"])) return false;
|
|
39
|
-
const align = (textStyle as { textAlignment?: unknown }).textAlignment;
|
|
40
|
-
return align === undefined || align === "AUTO";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
19
|
/** True when a plain text field can round-trip this Ricos document: no document styling, and
|
|
44
|
-
* only paragraphs of undecorated text whose paragraph data is the wire default
|
|
45
|
-
*
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
&& onlyKeys(n, ["type", "id", "nodes", "paragraphData"]) && defaultParagraphData(n.paragraphData)
|
|
51
|
-
&& (Array.isArray(n.nodes) ? (n.nodes as RicosNode[]) : []).every((t) => !!t && typeof t === "object" && t.type === "TEXT"
|
|
52
|
-
&& onlyKeys(t, ["type", "id", "textData", "nodes"]) && (!Array.isArray(t.nodes) || t.nodes.length === 0)
|
|
53
|
-
&& !(Array.isArray(t.textData?.decorations) && t.textData.decorations.length)));
|
|
54
|
-
}
|
|
20
|
+
* only paragraphs of undecorated text whose paragraph data is the wire default (Events v3 sends
|
|
21
|
+
* `{ textStyle: { textAlignment: "AUTO" }, indentation: 0 }` on every paragraph). Anything else —
|
|
22
|
+
* a heading, list, link, bold run, media node, alignment or indent — is rich, and so is a missing
|
|
23
|
+
* document (callers decide what an absent body means). */
|
|
24
|
+
export const ricosIsPlain = (doc: { nodes?: unknown; documentStyle?: unknown } | null | undefined): boolean =>
|
|
25
|
+
!!doc && isPlainRicos(doc);
|
|
55
26
|
|
|
56
27
|
/** Preview text for a legacy DraftJS document: its `blocks[].text` joined by a blank line, or ""
|
|
57
28
|
* when the payload is not DraftJS JSON (never a throw). */
|
|
@@ -10,8 +10,8 @@ import { defineService } from "@himalaya/state";
|
|
|
10
10
|
import { getEvent, updateEvent, type WixEvent } from "@himalaya/wix-events";
|
|
11
11
|
import { normalizeEvent } from "../_shared/himi-events.js";
|
|
12
12
|
import { postBodyText, plainPostBody } from "../_shared/owner-post-content.js";
|
|
13
|
-
//
|
|
14
|
-
//
|
|
13
|
+
// Events v3 sends default paragraphData on EVERY paragraph and an empty documentStyle; ricosIsPlain
|
|
14
|
+
// reads both as plain.
|
|
15
15
|
import { ricosIsPlain } from "../_shared/owner-rich-guard.js";
|
|
16
16
|
import { ownerLoadError, isBridgeUnavailable, isOwnerPreviewDemo, shouldQueueOwnerWrite } from "../_shared/owner-load-error.js";
|
|
17
17
|
import { DEMO_EVENTS, DEMO_NOTE, DEMO_SAVE_NOTE, demoRefusalPatch } from "../_shared/owner-preview-demo.js";
|
|
@@ -1,35 +1,16 @@
|
|
|
1
1
|
import type { WixRichContent } from "@himalaya/wix-blog";
|
|
2
|
+
// The rich-content helpers. Inside the platform source this import is a relative path, so node test runs
|
|
3
|
+
// resolve it; an app's copy imports "@himalaya/wix-rich-content".
|
|
4
|
+
import { ricosToPlainText, isPlainRicos, plainTextToRicos } from "@himalaya/wix-rich-content";
|
|
2
5
|
import { wixMediaUrl, WIX_DETAIL_IMAGE } from "./himi-store.js";
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
// Thin wrappers keep the WixRichContent signatures callers compile against.
|
|
5
8
|
/** Join inline runs without adding paragraph breaks between bold/plain spans. */
|
|
6
|
-
export
|
|
7
|
-
const inline = (node: Node): string => typeof node.textData?.text === "string"
|
|
8
|
-
? node.textData.text : (node.nodes ?? []).map(inline).join("");
|
|
9
|
-
const blocks = (nodes: Node[]): string[] => nodes.flatMap(node =>
|
|
10
|
-
node.type === "PARAGRAPH" || node.type === "HEADING" || node.type === "TEXT"
|
|
11
|
-
? [inline(node)] : blocks(node.nodes ?? []));
|
|
12
|
-
return blocks((rich?.nodes ?? []) as Node[]).join("\n\n");
|
|
13
|
-
}
|
|
9
|
+
export const postBodyText = (rich: WixRichContent | null | undefined): string => ricosToPlainText(rich);
|
|
14
10
|
/** The plain editor must not overwrite formatting or any opaque media node.
|
|
15
11
|
* Rich drafts remain editable for title/excerpt/cover; body stays untouched. */
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
if (!raw || typeof raw !== "object") return true;
|
|
19
|
-
const node = raw as Node;
|
|
20
|
-
if (node.type !== "PARAGRAPH") return true;
|
|
21
|
-
if (Object.keys(node).some(key => !["type", "id", "nodes", "paragraphData"].includes(key))) return true;
|
|
22
|
-
if (node.paragraphData && Object.keys(node.paragraphData as object).length) return true;
|
|
23
|
-
return (node.nodes ?? []).some(child => child.type !== "TEXT"
|
|
24
|
-
|| !!child.textData?.decorations?.length
|
|
25
|
-
|| Object.keys(child).some(key => !["type", "id", "textData", "nodes"].includes(key)));
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
export function plainPostBody(body: string): WixRichContent {
|
|
29
|
-
return { nodes: body.split("\n\n").map(text => ({
|
|
30
|
-
type: "PARAGRAPH", nodes: [{ type: "TEXT", textData: { text, decorations: [] } }],
|
|
31
|
-
})) };
|
|
32
|
-
}
|
|
12
|
+
export const hasRichBody = (rich: WixRichContent | null | undefined): boolean => !isPlainRicos(rich);
|
|
13
|
+
export const plainPostBody = (body: string): WixRichContent => plainTextToRicos(body);
|
|
33
14
|
/** Current Blog media shape plus older coverImage projections. */
|
|
34
15
|
export function postCoverUrl(raw: Record<string, any> | null | undefined): string {
|
|
35
16
|
if (!raw) return "";
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// May a plain owner form round-trip this body? The Ricos and HTML checks are @himalaya/wix-rich-content's;
|
|
2
|
+
// this file adds the read-only previews the forms show for a locked body, and the FAQ's legacy DraftJS answer.
|
|
3
|
+
// Inside the platform source this import is a relative path, so node test runs resolve it; an app's copy
|
|
4
|
+
// imports "@himalaya/wix-rich-content".
|
|
5
|
+
import { htmlIsTrivial, isPlainRicos } from "@himalaya/wix-rich-content";
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
const TAG = /<[a-z!/?]/i;
|
|
7
|
-
|
|
8
|
-
/** True when a plain text field can hold this HTML without losing anything: empty, bare text
|
|
9
|
-
* (no tag — a lone `<` in prose is text), or exactly one attribute-less `<p>…</p>` with no
|
|
10
|
-
* inner tags. */
|
|
11
|
-
export function htmlIsTrivial(html: string | null | undefined): boolean {
|
|
12
|
-
const s = (html ?? "").trim();
|
|
13
|
-
return !s || !TAG.test(s) || /^<p>[^<>]*<\/p>$/.test(s);
|
|
14
|
-
}
|
|
7
|
+
export { htmlIsTrivial };
|
|
15
8
|
|
|
16
9
|
const ENTITIES: Record<string, string> = { " ": " ", "&": "&", "<": "<", ">": ">", """: "\"", "'": "'" };
|
|
17
10
|
|
|
@@ -23,35 +16,13 @@ export function htmlPreviewText(html: string | null | undefined): string {
|
|
|
23
16
|
.split("\n").map((l) => l.replace(/\s+/g, " ").trim()).filter(Boolean).join("\n");
|
|
24
17
|
}
|
|
25
18
|
|
|
26
|
-
type RicosNode = { type?: unknown; nodes?: unknown; textData?: { decorations?: unknown }; paragraphData?: unknown; [key: string]: unknown };
|
|
27
|
-
const onlyKeys = (o: object, allowed: string[]) => Object.keys(o).every((k) => allowed.includes(k));
|
|
28
|
-
const emptyObject = (v: unknown) => v === undefined || (!!v && typeof v === "object" && Object.keys(v as object).length === 0);
|
|
29
|
-
|
|
30
|
-
/** The paragraph styling a PLAIN paragraph still carries on the Ricos wire (Events v3 sends
|
|
31
|
-
* `{ textStyle: { textAlignment: "AUTO" }, indentation: 0 }` on every paragraph). */
|
|
32
|
-
function defaultParagraphData(pd: unknown): boolean {
|
|
33
|
-
if (pd === undefined) return true;
|
|
34
|
-
if (!pd || typeof pd !== "object" || !onlyKeys(pd, ["textStyle", "indentation"])) return false;
|
|
35
|
-
const { textStyle, indentation } = pd as { textStyle?: unknown; indentation?: unknown };
|
|
36
|
-
if (indentation !== undefined && indentation !== 0) return false;
|
|
37
|
-
if (textStyle === undefined) return true;
|
|
38
|
-
if (!textStyle || typeof textStyle !== "object" || !onlyKeys(textStyle, ["textAlignment"])) return false;
|
|
39
|
-
const align = (textStyle as { textAlignment?: unknown }).textAlignment;
|
|
40
|
-
return align === undefined || align === "AUTO";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
19
|
/** True when a plain text field can round-trip this Ricos document: no document styling, and
|
|
44
|
-
* only paragraphs of undecorated text whose paragraph data is the wire default
|
|
45
|
-
*
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
&& onlyKeys(n, ["type", "id", "nodes", "paragraphData"]) && defaultParagraphData(n.paragraphData)
|
|
51
|
-
&& (Array.isArray(n.nodes) ? (n.nodes as RicosNode[]) : []).every((t) => !!t && typeof t === "object" && t.type === "TEXT"
|
|
52
|
-
&& onlyKeys(t, ["type", "id", "textData", "nodes"]) && (!Array.isArray(t.nodes) || t.nodes.length === 0)
|
|
53
|
-
&& !(Array.isArray(t.textData?.decorations) && t.textData.decorations.length)));
|
|
54
|
-
}
|
|
20
|
+
* only paragraphs of undecorated text whose paragraph data is the wire default (Events v3 sends
|
|
21
|
+
* `{ textStyle: { textAlignment: "AUTO" }, indentation: 0 }` on every paragraph). Anything else —
|
|
22
|
+
* a heading, list, link, bold run, media node, alignment or indent — is rich, and so is a missing
|
|
23
|
+
* document (callers decide what an absent body means). */
|
|
24
|
+
export const ricosIsPlain = (doc: { nodes?: unknown; documentStyle?: unknown } | null | undefined): boolean =>
|
|
25
|
+
!!doc && isPlainRicos(doc);
|
|
55
26
|
|
|
56
27
|
/** Preview text for a legacy DraftJS document: its `blocks[].text` joined by a blank line, or ""
|
|
57
28
|
* when the payload is not DraftJS JSON (never a throw). */
|
|
@@ -1,35 +1,16 @@
|
|
|
1
1
|
import type { WixRichContent } from "@himalaya/wix-blog";
|
|
2
|
+
// The rich-content helpers. Inside the platform source this import is a relative path, so node test runs
|
|
3
|
+
// resolve it; an app's copy imports "@himalaya/wix-rich-content".
|
|
4
|
+
import { ricosToPlainText, isPlainRicos, plainTextToRicos } from "@himalaya/wix-rich-content";
|
|
2
5
|
import { wixMediaUrl, WIX_DETAIL_IMAGE } from "./himi-store.js";
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
// Thin wrappers keep the WixRichContent signatures callers compile against.
|
|
5
8
|
/** Join inline runs without adding paragraph breaks between bold/plain spans. */
|
|
6
|
-
export
|
|
7
|
-
const inline = (node: Node): string => typeof node.textData?.text === "string"
|
|
8
|
-
? node.textData.text : (node.nodes ?? []).map(inline).join("");
|
|
9
|
-
const blocks = (nodes: Node[]): string[] => nodes.flatMap(node =>
|
|
10
|
-
node.type === "PARAGRAPH" || node.type === "HEADING" || node.type === "TEXT"
|
|
11
|
-
? [inline(node)] : blocks(node.nodes ?? []));
|
|
12
|
-
return blocks((rich?.nodes ?? []) as Node[]).join("\n\n");
|
|
13
|
-
}
|
|
9
|
+
export const postBodyText = (rich: WixRichContent | null | undefined): string => ricosToPlainText(rich);
|
|
14
10
|
/** The plain editor must not overwrite formatting or any opaque media node.
|
|
15
11
|
* Rich drafts remain editable for title/excerpt/cover; body stays untouched. */
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
if (!raw || typeof raw !== "object") return true;
|
|
19
|
-
const node = raw as Node;
|
|
20
|
-
if (node.type !== "PARAGRAPH") return true;
|
|
21
|
-
if (Object.keys(node).some(key => !["type", "id", "nodes", "paragraphData"].includes(key))) return true;
|
|
22
|
-
if (node.paragraphData && Object.keys(node.paragraphData as object).length) return true;
|
|
23
|
-
return (node.nodes ?? []).some(child => child.type !== "TEXT"
|
|
24
|
-
|| !!child.textData?.decorations?.length
|
|
25
|
-
|| Object.keys(child).some(key => !["type", "id", "textData", "nodes"].includes(key)));
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
export function plainPostBody(body: string): WixRichContent {
|
|
29
|
-
return { nodes: body.split("\n\n").map(text => ({
|
|
30
|
-
type: "PARAGRAPH", nodes: [{ type: "TEXT", textData: { text, decorations: [] } }],
|
|
31
|
-
})) };
|
|
32
|
-
}
|
|
12
|
+
export const hasRichBody = (rich: WixRichContent | null | undefined): boolean => !isPlainRicos(rich);
|
|
13
|
+
export const plainPostBody = (body: string): WixRichContent => plainTextToRicos(body);
|
|
33
14
|
/** Current Blog media shape plus older coverImage projections. */
|
|
34
15
|
export function postCoverUrl(raw: Record<string, any> | null | undefined): string {
|
|
35
16
|
if (!raw) return "";
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// May a plain owner form round-trip this body? The Ricos and HTML checks are @himalaya/wix-rich-content's;
|
|
2
|
+
// this file adds the read-only previews the forms show for a locked body, and the FAQ's legacy DraftJS answer.
|
|
3
|
+
// Inside the platform source this import is a relative path, so node test runs resolve it; an app's copy
|
|
4
|
+
// imports "@himalaya/wix-rich-content".
|
|
5
|
+
import { htmlIsTrivial, isPlainRicos } from "@himalaya/wix-rich-content";
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
const TAG = /<[a-z!/?]/i;
|
|
7
|
-
|
|
8
|
-
/** True when a plain text field can hold this HTML without losing anything: empty, bare text
|
|
9
|
-
* (no tag — a lone `<` in prose is text), or exactly one attribute-less `<p>…</p>` with no
|
|
10
|
-
* inner tags. */
|
|
11
|
-
export function htmlIsTrivial(html: string | null | undefined): boolean {
|
|
12
|
-
const s = (html ?? "").trim();
|
|
13
|
-
return !s || !TAG.test(s) || /^<p>[^<>]*<\/p>$/.test(s);
|
|
14
|
-
}
|
|
7
|
+
export { htmlIsTrivial };
|
|
15
8
|
|
|
16
9
|
const ENTITIES: Record<string, string> = { " ": " ", "&": "&", "<": "<", ">": ">", """: "\"", "'": "'" };
|
|
17
10
|
|
|
@@ -23,35 +16,13 @@ export function htmlPreviewText(html: string | null | undefined): string {
|
|
|
23
16
|
.split("\n").map((l) => l.replace(/\s+/g, " ").trim()).filter(Boolean).join("\n");
|
|
24
17
|
}
|
|
25
18
|
|
|
26
|
-
type RicosNode = { type?: unknown; nodes?: unknown; textData?: { decorations?: unknown }; paragraphData?: unknown; [key: string]: unknown };
|
|
27
|
-
const onlyKeys = (o: object, allowed: string[]) => Object.keys(o).every((k) => allowed.includes(k));
|
|
28
|
-
const emptyObject = (v: unknown) => v === undefined || (!!v && typeof v === "object" && Object.keys(v as object).length === 0);
|
|
29
|
-
|
|
30
|
-
/** The paragraph styling a PLAIN paragraph still carries on the Ricos wire (Events v3 sends
|
|
31
|
-
* `{ textStyle: { textAlignment: "AUTO" }, indentation: 0 }` on every paragraph). */
|
|
32
|
-
function defaultParagraphData(pd: unknown): boolean {
|
|
33
|
-
if (pd === undefined) return true;
|
|
34
|
-
if (!pd || typeof pd !== "object" || !onlyKeys(pd, ["textStyle", "indentation"])) return false;
|
|
35
|
-
const { textStyle, indentation } = pd as { textStyle?: unknown; indentation?: unknown };
|
|
36
|
-
if (indentation !== undefined && indentation !== 0) return false;
|
|
37
|
-
if (textStyle === undefined) return true;
|
|
38
|
-
if (!textStyle || typeof textStyle !== "object" || !onlyKeys(textStyle, ["textAlignment"])) return false;
|
|
39
|
-
const align = (textStyle as { textAlignment?: unknown }).textAlignment;
|
|
40
|
-
return align === undefined || align === "AUTO";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
19
|
/** True when a plain text field can round-trip this Ricos document: no document styling, and
|
|
44
|
-
* only paragraphs of undecorated text whose paragraph data is the wire default
|
|
45
|
-
*
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
&& onlyKeys(n, ["type", "id", "nodes", "paragraphData"]) && defaultParagraphData(n.paragraphData)
|
|
51
|
-
&& (Array.isArray(n.nodes) ? (n.nodes as RicosNode[]) : []).every((t) => !!t && typeof t === "object" && t.type === "TEXT"
|
|
52
|
-
&& onlyKeys(t, ["type", "id", "textData", "nodes"]) && (!Array.isArray(t.nodes) || t.nodes.length === 0)
|
|
53
|
-
&& !(Array.isArray(t.textData?.decorations) && t.textData.decorations.length)));
|
|
54
|
-
}
|
|
20
|
+
* only paragraphs of undecorated text whose paragraph data is the wire default (Events v3 sends
|
|
21
|
+
* `{ textStyle: { textAlignment: "AUTO" }, indentation: 0 }` on every paragraph). Anything else —
|
|
22
|
+
* a heading, list, link, bold run, media node, alignment or indent — is rich, and so is a missing
|
|
23
|
+
* document (callers decide what an absent body means). */
|
|
24
|
+
export const ricosIsPlain = (doc: { nodes?: unknown; documentStyle?: unknown } | null | undefined): boolean =>
|
|
25
|
+
!!doc && isPlainRicos(doc);
|
|
55
26
|
|
|
56
27
|
/** Preview text for a legacy DraftJS document: its `blocks[].text` joined by a blank line, or ""
|
|
57
28
|
* when the payload is not DraftJS JSON (never a throw). */
|
|
@@ -10,8 +10,8 @@ import { defineService } from "@himalaya/state";
|
|
|
10
10
|
import { getEvent, updateEvent, type WixEvent } from "@himalaya/wix-events";
|
|
11
11
|
import { normalizeEvent } from "../_shared/himi-events.js";
|
|
12
12
|
import { postBodyText, plainPostBody } from "../_shared/owner-post-content.js";
|
|
13
|
-
//
|
|
14
|
-
//
|
|
13
|
+
// Events v3 sends default paragraphData on EVERY paragraph and an empty documentStyle; ricosIsPlain
|
|
14
|
+
// reads both as plain.
|
|
15
15
|
import { ricosIsPlain } from "../_shared/owner-rich-guard.js";
|
|
16
16
|
import { ownerLoadError, isBridgeUnavailable, isOwnerPreviewDemo, shouldQueueOwnerWrite } from "../_shared/owner-load-error.js";
|
|
17
17
|
import { DEMO_EVENTS, DEMO_NOTE, DEMO_SAVE_NOTE, demoRefusalPatch } from "../_shared/owner-preview-demo.js";
|
|
@@ -1,35 +1,16 @@
|
|
|
1
1
|
import type { WixRichContent } from "@himalaya/wix-blog";
|
|
2
|
+
// The rich-content helpers. Inside the platform source this import is a relative path, so node test runs
|
|
3
|
+
// resolve it; an app's copy imports "@himalaya/wix-rich-content".
|
|
4
|
+
import { ricosToPlainText, isPlainRicos, plainTextToRicos } from "@himalaya/wix-rich-content";
|
|
2
5
|
import { wixMediaUrl, WIX_DETAIL_IMAGE } from "./himi-store.js";
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
// Thin wrappers keep the WixRichContent signatures callers compile against.
|
|
5
8
|
/** Join inline runs without adding paragraph breaks between bold/plain spans. */
|
|
6
|
-
export
|
|
7
|
-
const inline = (node: Node): string => typeof node.textData?.text === "string"
|
|
8
|
-
? node.textData.text : (node.nodes ?? []).map(inline).join("");
|
|
9
|
-
const blocks = (nodes: Node[]): string[] => nodes.flatMap(node =>
|
|
10
|
-
node.type === "PARAGRAPH" || node.type === "HEADING" || node.type === "TEXT"
|
|
11
|
-
? [inline(node)] : blocks(node.nodes ?? []));
|
|
12
|
-
return blocks((rich?.nodes ?? []) as Node[]).join("\n\n");
|
|
13
|
-
}
|
|
9
|
+
export const postBodyText = (rich: WixRichContent | null | undefined): string => ricosToPlainText(rich);
|
|
14
10
|
/** The plain editor must not overwrite formatting or any opaque media node.
|
|
15
11
|
* Rich drafts remain editable for title/excerpt/cover; body stays untouched. */
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
if (!raw || typeof raw !== "object") return true;
|
|
19
|
-
const node = raw as Node;
|
|
20
|
-
if (node.type !== "PARAGRAPH") return true;
|
|
21
|
-
if (Object.keys(node).some(key => !["type", "id", "nodes", "paragraphData"].includes(key))) return true;
|
|
22
|
-
if (node.paragraphData && Object.keys(node.paragraphData as object).length) return true;
|
|
23
|
-
return (node.nodes ?? []).some(child => child.type !== "TEXT"
|
|
24
|
-
|| !!child.textData?.decorations?.length
|
|
25
|
-
|| Object.keys(child).some(key => !["type", "id", "textData", "nodes"].includes(key)));
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
export function plainPostBody(body: string): WixRichContent {
|
|
29
|
-
return { nodes: body.split("\n\n").map(text => ({
|
|
30
|
-
type: "PARAGRAPH", nodes: [{ type: "TEXT", textData: { text, decorations: [] } }],
|
|
31
|
-
})) };
|
|
32
|
-
}
|
|
12
|
+
export const hasRichBody = (rich: WixRichContent | null | undefined): boolean => !isPlainRicos(rich);
|
|
13
|
+
export const plainPostBody = (body: string): WixRichContent => plainTextToRicos(body);
|
|
33
14
|
/** Current Blog media shape plus older coverImage projections. */
|
|
34
15
|
export function postCoverUrl(raw: Record<string, any> | null | undefined): string {
|
|
35
16
|
if (!raw) return "";
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// May a plain owner form round-trip this body? The Ricos and HTML checks are @himalaya/wix-rich-content's;
|
|
2
|
+
// this file adds the read-only previews the forms show for a locked body, and the FAQ's legacy DraftJS answer.
|
|
3
|
+
// Inside the platform source this import is a relative path, so node test runs resolve it; an app's copy
|
|
4
|
+
// imports "@himalaya/wix-rich-content".
|
|
5
|
+
import { htmlIsTrivial, isPlainRicos } from "@himalaya/wix-rich-content";
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
const TAG = /<[a-z!/?]/i;
|
|
7
|
-
|
|
8
|
-
/** True when a plain text field can hold this HTML without losing anything: empty, bare text
|
|
9
|
-
* (no tag — a lone `<` in prose is text), or exactly one attribute-less `<p>…</p>` with no
|
|
10
|
-
* inner tags. */
|
|
11
|
-
export function htmlIsTrivial(html: string | null | undefined): boolean {
|
|
12
|
-
const s = (html ?? "").trim();
|
|
13
|
-
return !s || !TAG.test(s) || /^<p>[^<>]*<\/p>$/.test(s);
|
|
14
|
-
}
|
|
7
|
+
export { htmlIsTrivial };
|
|
15
8
|
|
|
16
9
|
const ENTITIES: Record<string, string> = { " ": " ", "&": "&", "<": "<", ">": ">", """: "\"", "'": "'" };
|
|
17
10
|
|
|
@@ -23,35 +16,13 @@ export function htmlPreviewText(html: string | null | undefined): string {
|
|
|
23
16
|
.split("\n").map((l) => l.replace(/\s+/g, " ").trim()).filter(Boolean).join("\n");
|
|
24
17
|
}
|
|
25
18
|
|
|
26
|
-
type RicosNode = { type?: unknown; nodes?: unknown; textData?: { decorations?: unknown }; paragraphData?: unknown; [key: string]: unknown };
|
|
27
|
-
const onlyKeys = (o: object, allowed: string[]) => Object.keys(o).every((k) => allowed.includes(k));
|
|
28
|
-
const emptyObject = (v: unknown) => v === undefined || (!!v && typeof v === "object" && Object.keys(v as object).length === 0);
|
|
29
|
-
|
|
30
|
-
/** The paragraph styling a PLAIN paragraph still carries on the Ricos wire (Events v3 sends
|
|
31
|
-
* `{ textStyle: { textAlignment: "AUTO" }, indentation: 0 }` on every paragraph). */
|
|
32
|
-
function defaultParagraphData(pd: unknown): boolean {
|
|
33
|
-
if (pd === undefined) return true;
|
|
34
|
-
if (!pd || typeof pd !== "object" || !onlyKeys(pd, ["textStyle", "indentation"])) return false;
|
|
35
|
-
const { textStyle, indentation } = pd as { textStyle?: unknown; indentation?: unknown };
|
|
36
|
-
if (indentation !== undefined && indentation !== 0) return false;
|
|
37
|
-
if (textStyle === undefined) return true;
|
|
38
|
-
if (!textStyle || typeof textStyle !== "object" || !onlyKeys(textStyle, ["textAlignment"])) return false;
|
|
39
|
-
const align = (textStyle as { textAlignment?: unknown }).textAlignment;
|
|
40
|
-
return align === undefined || align === "AUTO";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
19
|
/** True when a plain text field can round-trip this Ricos document: no document styling, and
|
|
44
|
-
* only paragraphs of undecorated text whose paragraph data is the wire default
|
|
45
|
-
*
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
&& onlyKeys(n, ["type", "id", "nodes", "paragraphData"]) && defaultParagraphData(n.paragraphData)
|
|
51
|
-
&& (Array.isArray(n.nodes) ? (n.nodes as RicosNode[]) : []).every((t) => !!t && typeof t === "object" && t.type === "TEXT"
|
|
52
|
-
&& onlyKeys(t, ["type", "id", "textData", "nodes"]) && (!Array.isArray(t.nodes) || t.nodes.length === 0)
|
|
53
|
-
&& !(Array.isArray(t.textData?.decorations) && t.textData.decorations.length)));
|
|
54
|
-
}
|
|
20
|
+
* only paragraphs of undecorated text whose paragraph data is the wire default (Events v3 sends
|
|
21
|
+
* `{ textStyle: { textAlignment: "AUTO" }, indentation: 0 }` on every paragraph). Anything else —
|
|
22
|
+
* a heading, list, link, bold run, media node, alignment or indent — is rich, and so is a missing
|
|
23
|
+
* document (callers decide what an absent body means). */
|
|
24
|
+
export const ricosIsPlain = (doc: { nodes?: unknown; documentStyle?: unknown } | null | undefined): boolean =>
|
|
25
|
+
!!doc && isPlainRicos(doc);
|
|
55
26
|
|
|
56
27
|
/** Preview text for a legacy DraftJS document: its `blocks[].text` joined by a blank line, or ""
|
|
57
28
|
* when the payload is not DraftJS JSON (never a throw). */
|