@useinsider/guido 3.18.2 → 3.19.0-beta.5355c3f
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 +20 -0
- package/dist/components/Guido.vue.js +3 -3
- package/dist/components/Guido.vue2.js +67 -61
- package/dist/components/organisms/save-as-template/SaveAsTemplateDrawer.vue2.js +34 -34
- package/dist/composables/useActionsApi.js +24 -20
- package/dist/composables/useCortexBlueprintBridge.js +115 -54
- package/dist/composables/useEmailTemplateApplier.js +93 -50
- package/dist/composables/useGuidoStateBridge.js +29 -24
- package/dist/composables/useHttp.js +24 -22
- package/dist/composables/useStripo.js +7 -7
- package/dist/extensions/Blocks/Recommendation/controls/cardComposition/index.js +4 -4
- package/dist/extensions/Blocks/Recommendation/recommendation.css.js +6 -3
- package/dist/guido.css +1 -1
- package/dist/library.js +15 -13
- package/dist/package.json.js +1 -1
- package/dist/public/editorState.js +4 -0
- package/dist/services/templateLibraryApi.js +17 -14
- package/dist/src/composables/useCortexBlueprintBridge.d.ts +4 -3
- package/dist/src/composables/useEmailTemplateApplier.d.ts +22 -2
- package/dist/src/composables/useGuidoStateBridge.d.ts +11 -7
- package/dist/src/library.d.ts +2 -0
- package/dist/src/public/editorState.d.ts +14 -0
- package/dist/src/public/trackingIds.d.ts +6 -2
- package/dist/src/stores/editor.d.ts +25 -0
- package/dist/src/stores/guido-email-editor.d.ts +49 -27
- package/dist/src/utils/genericUtil.d.ts +6 -0
- package/dist/src/utils/liquidDynamicContent.d.ts +20 -0
- package/dist/stores/editor.js +4 -0
- package/dist/stores/guido-email-editor.js +3 -5
- package/dist/utils/genericUtil.js +63 -54
- package/dist/utils/liquidDynamicContent.js +15 -0
- package/dist/utils/templatePreparation.js +37 -36
- package/package.json +10 -6
package/dist/guido.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@charset "UTF-8";.gap-16[data-v-5553d071],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-73199fa4] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-2cb418af] .in-progress-wrapper__progress p span:last-child{display:none!important}[data-v-2cb418af] .in-progress-description-status{display:none!important}.view-options-wrapper[data-v-87033101]{position:relative;display:inline-block}.new-tag[data-v-87033101]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-87033101] .guido__view-option-selection-desktop svg,[data-v-87033101] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-87033101] .in-segments-wrapper__button_selected,[data-v-87033101] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-87033101] .in-tooltip-wrapper-v2__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-fad98586]{gap:8px}.version-history__toolbar[data-v-fad98586]{gap:4px}.view-options-wrapper[data-v-6ebabfe4]{position:relative;display:inline-block}.new-tag[data-v-6ebabfe4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-6ebabfe4] .guido__verion-history-view-option-selection-desktop svg,[data-v-6ebabfe4] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-6ebabfe4] .in-segments-wrapper__button_selected,[data-v-6ebabfe4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-6ebabfe4] .in-tooltip-wrapper-v2__icon{cursor:pointer}.auto-save-toggle[data-v-2c964af4]{position:relative}.auto-save-toggle__info-box[data-v-2c964af4]{position:absolute;top:100%;left:0;z-index:10;width:280px}.editor-actions[data-v-1ca6baf0]{gap:4px}.header-wrapper[data-v-d11dd577]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-
|
|
1
|
+
@charset "UTF-8";.gap-16[data-v-5553d071],.gap-16[data-v-0e1b0c54]{gap:16px}[data-v-73199fa4] .in-button-v2__wrapper{line-height:0}[data-v-22226124] .in-segments-wrapper__button_selected,[data-v-22226124] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb;color:#0010ac;border-color:#0010ac}[data-v-2cb418af] .in-progress-wrapper__progress p span:last-child{display:none!important}[data-v-2cb418af] .in-progress-description-status{display:none!important}.view-options-wrapper[data-v-87033101]{position:relative;display:inline-block}.new-tag[data-v-87033101]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-87033101] .guido__view-option-selection-desktop svg,[data-v-87033101] .guido__view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-87033101] .in-segments-wrapper__button_selected,[data-v-87033101] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-87033101] .in-tooltip-wrapper-v2__icon{cursor:pointer}.editor-toolbar[data-v-173c3a40]{gap:4px}.version-history-item[data-v-ee4b9c3f]{flex-basis:200px}.version-history[data-v-fad98586]{gap:8px}.version-history__toolbar[data-v-fad98586]{gap:4px}.view-options-wrapper[data-v-6ebabfe4]{position:relative;display:inline-block}.new-tag[data-v-6ebabfe4]{position:absolute;top:-8px;right:-16px;z-index:10}[data-v-6ebabfe4] .guido__verion-history-view-option-selection-desktop svg,[data-v-6ebabfe4] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-6ebabfe4] .in-segments-wrapper__button_selected,[data-v-6ebabfe4] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}[data-v-6ebabfe4] .in-tooltip-wrapper-v2__icon{cursor:pointer}.auto-save-toggle[data-v-2c964af4]{position:relative}.auto-save-toggle__info-box[data-v-2c964af4]{position:absolute;top:100%;left:0;z-index:10;width:280px}.editor-actions[data-v-1ca6baf0]{gap:4px}.header-wrapper[data-v-d11dd577]{min-width:1000px}.guido-loading__wrapper[data-v-07c4b2d8]{height:100%;top:75px!important;bottom:0!important}.guido-editor__wrapper[data-v-7674b645]{--ribbon-offset: 0px;position:relative;width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__container[data-v-7674b645]{width:100%;height:calc(100vh - 128px - var(--ribbon-offset))}.guido-editor__no-header[data-v-7674b645]{height:calc(100vh - 75px - var(--ribbon-offset))}[data-v-293f1c47] .in-breadcrumb-wrapper__links{cursor:pointer}.templates-wrapper[data-v-91a26acd]{gap:16px;grid-template-columns:repeat(3,1fr)}.templates-wrapper .template-wrapper[data-v-91a26acd]{cursor:pointer}.templates-wrapper .template-wrapper .template-container[data-v-91a26acd]{height:274px;padding:2px;transition:none}.templates-wrapper .template-wrapper .template-container.selected[data-v-91a26acd]{padding:1px}.templates-wrapper .template-wrapper .template-container .thumbnail[data-v-91a26acd]{object-fit:cover;transform:scale(1)}[data-v-b9a93c6e] .guido__verion-history-view-option-selection-desktop svg,[data-v-b9a93c6e] .guido__verion-history-view-option-selection-mobile svg{margin:0 0 0 2px}[data-v-b9a93c6e] .in-segments-wrapper__button_selected,[data-v-b9a93c6e] .in-segments-wrapper__button_selected:hover{background-color:#dae1fb}.error-list[data-v-c3fd5d4b]{gap:16px}.desktop-browser-header[data-v-d86c5af5]{height:79px;min-height:79px}.desktop-browser-header__left[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:378px}.desktop-browser-header__center[data-v-d86c5af5]{height:79px;background-repeat:repeat-x;background-size:auto 100%;background-position:left top}.desktop-browser-header__right[data-v-d86c5af5]{-webkit-user-drag:none;height:79px;width:112px}.desktop-preview[data-v-cf946232]{min-width:602px;height:70vh;min-height:583px;border-radius:10px}.desktop-preview iframe[data-v-cf946232]{min-height:504px}.iframe-wrapper[data-v-f255b2bb]{width:258px}.iframe-scaled[data-v-f255b2bb]{width:320px;height:124.0310077519%;transform:scale(.80625);transform-origin:top left}.cropped-text[data-v-eb3d05d7]{width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mobile-preview-wrapper__phone[data-v-3f472f96]{width:282px}.mobile-preview-wrapper__phone img[data-v-3f472f96]{object-fit:cover;border-radius:44px}.mobile-preview-wrapper__content[data-v-3f472f96]{width:258px;height:450px;left:12px}[data-v-6bcaca8a] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-f16f20f8] .vueperslides__bullets{pointer-events:none!important}[data-v-f16f20f8] .vueperslides__parallax-wrapper{padding-bottom:110px!important}[data-v-bb3cca55] .vueperslides__bullets{pointer-events:none!important}[data-v-bb3cca55] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-b09e80c4] .vueperslides__bullets{pointer-events:none!important}[data-v-b09e80c4] .vueperslides__parallax-wrapper{height:110px!important;padding-bottom:110px!important}[data-v-9bdef600] .vueperslides__parallax-wrapper{padding-bottom:110px!important}.assign-strategy-drawer-content .assign-strategy-select[data-v-2580a7d9]{width:100%;max-width:none}.assign-strategy-drawer-content .assign-strategy-select[data-v-2580a7d9] .in-dropdown-box__container{width:413px}.assign-strategy-drawer .spaced-dot-sep{background-image:linear-gradient(90deg,#91949f 20%,#fff0 0);background-position:center bottom;background-repeat:repeat-x;background-size:5px 1px}.assign-strategy-drawer .spaced-dot-sep--top{background-position:center top}.assign-strategy-drawer .spaced-dot-sep--fc-n:first-child{background:none}
|
package/dist/library.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import { default as
|
|
2
|
-
import { MessageType as
|
|
1
|
+
import { default as a } from "./components/Guido.vue.js";
|
|
2
|
+
import { MessageType as t, ProductType as d } from "./@types/config/schemas.js";
|
|
3
3
|
import "./@types/config/defaults.js";
|
|
4
|
-
import { getValidationErrors as
|
|
5
|
-
import { assignTrackingIds as
|
|
4
|
+
import { getValidationErrors as s, isValidConfig as p, parseConfig as g, parseConfigSafe as m, validateConfig as n } from "./@types/config/validator.js";
|
|
5
|
+
import { assignTrackingIds as u, readTrackFloor as x, readTrackSeq as T } from "./utils/linkTrackingIds.js";
|
|
6
|
+
import { useGuidoEmailEditorStore as C } from "./stores/guido-email-editor.js";
|
|
6
7
|
export {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
a as Guido,
|
|
9
|
+
t as MessageType,
|
|
10
|
+
d as ProductType,
|
|
11
|
+
u as assignTrackingIds,
|
|
12
|
+
s as getValidationErrors,
|
|
12
13
|
p as isValidConfig,
|
|
13
14
|
g as parseConfig,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
m as parseConfigSafe,
|
|
16
|
+
x as readTrackFloor,
|
|
17
|
+
T as readTrackSeq,
|
|
18
|
+
C as useGuidoEmailEditorStore,
|
|
19
|
+
n as validateConfig
|
|
18
20
|
};
|
package/dist/package.json.js
CHANGED
|
@@ -4,13 +4,14 @@ import { useToaster as C } from "../composables/useToaster.js";
|
|
|
4
4
|
import { useTranslations as h } from "../composables/useTranslations.js";
|
|
5
5
|
import { useLiquidValidator as S } from "../composables/validators/useLiquidValidator.js";
|
|
6
6
|
import { EditorType as v } from "../enums/defaults.js";
|
|
7
|
-
import { ToasterTypeOptions as
|
|
8
|
-
import { useRecommendationExtensionStore as
|
|
9
|
-
import { useSaveAsTemplateStore as
|
|
7
|
+
import { ToasterTypeOptions as x } from "../enums/toaster.js";
|
|
8
|
+
import { useRecommendationExtensionStore as E } from "../extensions/Blocks/Recommendation/store/recommendation.js";
|
|
9
|
+
import { useSaveAsTemplateStore as N } from "../stores/save-as-template.js";
|
|
10
10
|
import { base64EncodeWithSpecialChars as r } from "../utils/base64.js";
|
|
11
|
+
import { isPlainObject as P } from "../utils/genericUtil.js";
|
|
11
12
|
import { useTemplatePreparation as A } from "../utils/templatePreparation.js";
|
|
12
|
-
const
|
|
13
|
-
const { get: n, post: m } = b(), { handleError: o, showToaster: l } = C(), { config: c, isFeatureEnabled: p } = w(), { validateLiquidSyntax:
|
|
13
|
+
const B = () => {
|
|
14
|
+
const { get: n, post: m } = b(), { handleError: o, showToaster: l } = C(), { config: c, isFeatureEnabled: p } = w(), { validateLiquidSyntax: f } = S(), u = h();
|
|
14
15
|
return {
|
|
15
16
|
getCategories: async () => {
|
|
16
17
|
try {
|
|
@@ -40,10 +41,10 @@ const j = () => {
|
|
|
40
41
|
createTemplate: async () => {
|
|
41
42
|
var e, t, d, y;
|
|
42
43
|
try {
|
|
43
|
-
const { prepareTemplateDetails: i } = A(), s =
|
|
44
|
-
if (p("liquidSyntax") && !await
|
|
44
|
+
const { prepareTemplateDetails: i } = A(), s = N(), a = await i();
|
|
45
|
+
if (p("liquidSyntax") && !await f(a.compiledHtml))
|
|
45
46
|
return !1;
|
|
46
|
-
const
|
|
47
|
+
const T = {
|
|
47
48
|
name: s.getTemplateName,
|
|
48
49
|
categories: s.getSelectedCategoryIds,
|
|
49
50
|
productId: ((t = (e = c.value) == null ? void 0 : e.partner) == null ? void 0 : t.productType) || 0,
|
|
@@ -54,7 +55,7 @@ const j = () => {
|
|
|
54
55
|
unsubscriptionPreferencePageStatus: !1,
|
|
55
56
|
unsubscriptionPreferencePages: [],
|
|
56
57
|
recommendationCampaignUrls: r(
|
|
57
|
-
|
|
58
|
+
E().recommendationCampaignUrls
|
|
58
59
|
),
|
|
59
60
|
recommendationConfigs: r({}),
|
|
60
61
|
isGuido: !0,
|
|
@@ -66,16 +67,18 @@ const j = () => {
|
|
|
66
67
|
css: a.css,
|
|
67
68
|
dynamicContentList: a.dynamicContentList
|
|
68
69
|
})
|
|
69
|
-
}, { data:
|
|
70
|
+
}, { data: g } = await m(
|
|
70
71
|
"/newsletter/template-library/create-template",
|
|
71
|
-
|
|
72
|
+
T
|
|
72
73
|
);
|
|
73
|
-
|
|
74
|
+
if (!P(g))
|
|
75
|
+
throw new Error("Unexpected create-template response");
|
|
76
|
+
return g.type === "alert" ? (l({
|
|
74
77
|
message: u(
|
|
75
78
|
"newsletter.template-library-save-alert",
|
|
76
79
|
{ templateName: s.getTemplateName }
|
|
77
80
|
),
|
|
78
|
-
type:
|
|
81
|
+
type: x.Error
|
|
79
82
|
}), !1) : (l({
|
|
80
83
|
message: u(
|
|
81
84
|
"newsletter.template-library-save-success",
|
|
@@ -90,5 +93,5 @@ const j = () => {
|
|
|
90
93
|
};
|
|
91
94
|
};
|
|
92
95
|
export {
|
|
93
|
-
|
|
96
|
+
B as useTemplateLibraryApi
|
|
94
97
|
};
|
|
@@ -17,12 +17,13 @@
|
|
|
17
17
|
* - `error` segments (and message-level `isError` / `isCancelled`) trigger
|
|
18
18
|
* a localized toaster.
|
|
19
19
|
*
|
|
20
|
+
* Every apply settles into a terminal `applyAcks` entry on the public store, so
|
|
21
|
+
* cortex can move its Apply button on the truth of the editor write rather than
|
|
22
|
+
* on the click.
|
|
23
|
+
*
|
|
20
24
|
* In dev (`import.meta.env.DEV`) every observed segment + message-level flag
|
|
21
25
|
* change is also `console.debug`-logged with the prefix `[guido:cortex]` so a
|
|
22
26
|
* developer can watch the agent stream in real time. In production this is
|
|
23
27
|
* gated behind `localStorage.guido:debug:ai === '1'`.
|
|
24
|
-
*
|
|
25
|
-
* If cortex-fe isn't loaded the chat store is undefined and the bridge is a
|
|
26
|
-
* no-op (intentional — Guido must still work standalone).
|
|
27
28
|
*/
|
|
28
29
|
export declare const useCortexBlueprintBridge: () => void;
|
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
type ApplyStatus = 'pending' | 'applying' | 'applied' | 'failed';
|
|
2
|
+
/**
|
|
3
|
+
* Terminal outcome of an apply. Every promise this composable hands out settles
|
|
4
|
+
* with exactly one of these — callers (the cortex bridge) ack on it, so a
|
|
5
|
+
* never-settling promise would strand cortex's Apply button forever.
|
|
6
|
+
*
|
|
7
|
+
* `superseded` means a newer payload for the same blueprint replaced this one:
|
|
8
|
+
* not a success, not a failure, and deliberately never acked.
|
|
9
|
+
*/
|
|
10
|
+
export type ApplyOutcome = {
|
|
11
|
+
status: 'applied';
|
|
12
|
+
} | {
|
|
13
|
+
status: 'failed';
|
|
14
|
+
error: unknown;
|
|
15
|
+
} | {
|
|
16
|
+
status: 'superseded';
|
|
17
|
+
};
|
|
2
18
|
/**
|
|
3
19
|
* Applies email_template blueprints from the chat agent to the Stripo editor.
|
|
4
20
|
*
|
|
@@ -9,6 +25,10 @@ type ApplyStatus = 'pending' | 'applying' | 'applied' | 'failed';
|
|
|
9
25
|
* - manual Apply click (first apply): immediate.
|
|
10
26
|
* - blueprint_update after the user has applied once: debounced 250 ms trailing
|
|
11
27
|
* so rapid mid-stream updates don't thrash updateHtmlAndCss.
|
|
28
|
+
*
|
|
29
|
+
* A click that lands before Stripo finished initializing is queued rather than
|
|
30
|
+
* dropped, and flushed when `isStripoInitialized` flips — the user does not
|
|
31
|
+
* have to click again.
|
|
12
32
|
*/
|
|
13
33
|
export declare const useEmailTemplateApplier: () => {
|
|
14
34
|
applyStatus: import("vue").Ref<Record<string, ApplyStatus>>;
|
|
@@ -16,11 +36,11 @@ export declare const useEmailTemplateApplier: () => {
|
|
|
16
36
|
applyTemplate: (blueprintId: string, data: {
|
|
17
37
|
html?: string;
|
|
18
38
|
css?: string;
|
|
19
|
-
}) =>
|
|
39
|
+
}) => Promise<ApplyOutcome>;
|
|
20
40
|
applyTemplateDebounced: (blueprintId: string, data: {
|
|
21
41
|
html?: string;
|
|
22
42
|
css?: string;
|
|
23
|
-
}) =>
|
|
43
|
+
}) => Promise<ApplyOutcome>;
|
|
24
44
|
};
|
|
25
45
|
export declare const resetEmailTemplateApplier: () => void;
|
|
26
46
|
export {};
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Outbound bridge — publishes the current editor's html/css to the shared
|
|
3
|
-
* Pinia store `guidoEmailEditor
|
|
4
|
-
*
|
|
3
|
+
* Pinia store `guidoEmailEditor`. cortex-fe reads it at chat-stream submit
|
|
4
|
+
* time and forwards it as `clientState.editor`, which is what enables
|
|
5
5
|
* sub-case 1.3 (refine an already-loaded template via the email-agent).
|
|
6
6
|
*
|
|
7
7
|
* The bridge writes on three triggers:
|
|
8
8
|
*
|
|
9
9
|
* 1. Stripo finishes initializing — first snapshot.
|
|
10
|
-
* 2.
|
|
11
|
-
* Stripo edit doesn't thrash the store.
|
|
10
|
+
* 2. Every document change, via the `changeSeq` counter — debounced 500 ms
|
|
11
|
+
* so a streaming Stripo edit doesn't thrash the store.
|
|
12
12
|
* 3. Editor `hasChanges` flips back to false (post-save / autosave) —
|
|
13
13
|
* immediate, since this is the canonical "saved" snapshot.
|
|
14
14
|
*
|
|
15
15
|
* `getTemplateData()` is a Stripo iframe call; it must not run before
|
|
16
16
|
* `isStripoInitialized` is true. The bridge guards against that.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* The store is written even with no reader attached — it has zero overhead,
|
|
19
|
+
* and a late reader still finds consistent state.
|
|
20
|
+
*
|
|
21
|
+
* Readers are not optional any more, so treat the publish timing as part of
|
|
22
|
+
* the contract: the store ships as `@useinsider/guido/editor-state`, and
|
|
23
|
+
* email-fe imports it to scan the live html for its Array of Objects lock.
|
|
24
|
+
* Weakening a trigger here silently breaks that lock in the consumer.
|
|
21
25
|
*/
|
|
22
26
|
export declare const useGuidoStateBridge: () => void;
|
package/dist/src/library.d.ts
CHANGED
|
@@ -2,5 +2,7 @@ export { default as Guido } from './components/Guido.vue';
|
|
|
2
2
|
export type { GuidoConfig, GuidoConfigInput, IdentityConfig, PartnerConfig, TemplateConfig, TemplateMigrationConfig, LegacyRecommendationConfig, EditorConfig, UIConfig, FeaturesConfig, BlocksConfig, CompilerConfig, DynamicContent, EmailHeader, DefaultBlockType, CustomBlockType, ValidationResult, ValidationError, } from './@types/config';
|
|
3
3
|
export { validateConfig, parseConfig, parseConfigSafe, isValidConfig, getValidationErrors, MessageType, ProductType, } from './@types/config';
|
|
4
4
|
export { assignTrackingIds, readTrackFloor, readTrackSeq } from './public/trackingIds';
|
|
5
|
+
export { useGuidoEmailEditorStore } from './public/editorState';
|
|
6
|
+
export type { GuidoEmailEditorState } from './public/editorState';
|
|
5
7
|
export type { StripoEventType } from './@types/events';
|
|
6
8
|
export type { PositionData, DynamicContentEvent, MergeTagClickEvent, MergeTag, BaseDynamicContent, DynamicContentNode, DynamicContentList, TooltipOptions, TextValueObject, L10n, } from './@types/generic';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Published as `@useinsider/guido/editor-state` — the live editor snapshot Guido
|
|
3
|
+
* writes into the host's shared Pinia singleton.
|
|
4
|
+
*
|
|
5
|
+
* Importing from here pulls in nothing but pinia, so the editor never enters a
|
|
6
|
+
* consumer's module graph. Re-export surface only: this is the contract,
|
|
7
|
+
* `stores/guido-email-editor` is the implementation and stays free to move.
|
|
8
|
+
*
|
|
9
|
+
* Consumers read the store by importing it; the id `guidoEmailEditor` remains
|
|
10
|
+
* part of the contract for cortex-fe, which probes `pinia._s` at runtime instead
|
|
11
|
+
* of taking a build-time dependency on this package.
|
|
12
|
+
*/
|
|
13
|
+
export { useGuidoEmailEditorStore } from '../stores/guido-email-editor';
|
|
14
|
+
export type { GuidoEmailEditorState } from '../stores/guido-email-editor';
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* Importing from here pulls in nothing else, so the editor never enters a
|
|
5
5
|
* consumer's module graph. Re-export surface only: this is the contract,
|
|
6
6
|
* `utils/linkTrackingIds` is the implementation and stays free to move.
|
|
7
|
+
*
|
|
8
|
+
* Relative, not the `@@/Utils/` alias: vue-tsc copies the specifier into the emitted
|
|
9
|
+
* .d.ts verbatim, and a consumer's TypeScript cannot resolve our alias table. A relative
|
|
10
|
+
* path resolves against dist/src/, so the published types actually load.
|
|
7
11
|
*/
|
|
8
|
-
export { assignTrackingIds, readTrackFloor, readTrackSeq, TRACK_ID_ATTRIBUTE, TRACK_SEQ_ATTRIBUTE, } from '
|
|
9
|
-
export type { TrackingIdResult } from '
|
|
12
|
+
export { assignTrackingIds, readTrackFloor, readTrackSeq, TRACK_ID_ATTRIBUTE, TRACK_SEQ_ATTRIBUTE, } from '../utils/linkTrackingIds';
|
|
13
|
+
export type { TrackingIdResult } from '../utils/linkTrackingIds';
|
|
@@ -6,6 +6,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
6
6
|
isPreviewModeOpen: boolean;
|
|
7
7
|
editorVisualMode: string;
|
|
8
8
|
hasChanges: boolean;
|
|
9
|
+
changeSeq: number;
|
|
9
10
|
isStripoInitialized: boolean;
|
|
10
11
|
templateId: string;
|
|
11
12
|
syncModulesEnabled: boolean;
|
|
@@ -19,6 +20,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
19
20
|
isPreviewModeOpen: boolean;
|
|
20
21
|
editorVisualMode: string;
|
|
21
22
|
hasChanges: boolean;
|
|
23
|
+
changeSeq: number;
|
|
22
24
|
isStripoInitialized: boolean;
|
|
23
25
|
templateId: string;
|
|
24
26
|
syncModulesEnabled: boolean;
|
|
@@ -31,6 +33,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
31
33
|
isPreviewModeOpen: boolean;
|
|
32
34
|
editorVisualMode: string;
|
|
33
35
|
hasChanges: boolean;
|
|
36
|
+
changeSeq: number;
|
|
34
37
|
isStripoInitialized: boolean;
|
|
35
38
|
templateId: string;
|
|
36
39
|
syncModulesEnabled: boolean;
|
|
@@ -44,6 +47,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
44
47
|
isPreviewModeOpen: boolean;
|
|
45
48
|
editorVisualMode: string;
|
|
46
49
|
hasChanges: boolean;
|
|
50
|
+
changeSeq: number;
|
|
47
51
|
isStripoInitialized: boolean;
|
|
48
52
|
templateId: string;
|
|
49
53
|
syncModulesEnabled: boolean;
|
|
@@ -56,6 +60,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
56
60
|
isPreviewModeOpen: boolean;
|
|
57
61
|
editorVisualMode: string;
|
|
58
62
|
hasChanges: boolean;
|
|
63
|
+
changeSeq: number;
|
|
59
64
|
isStripoInitialized: boolean;
|
|
60
65
|
templateId: string;
|
|
61
66
|
syncModulesEnabled: boolean;
|
|
@@ -69,6 +74,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
69
74
|
isPreviewModeOpen: boolean;
|
|
70
75
|
editorVisualMode: string;
|
|
71
76
|
hasChanges: boolean;
|
|
77
|
+
changeSeq: number;
|
|
72
78
|
isStripoInitialized: boolean;
|
|
73
79
|
templateId: string;
|
|
74
80
|
syncModulesEnabled: boolean;
|
|
@@ -81,6 +87,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
81
87
|
isPreviewModeOpen: boolean;
|
|
82
88
|
editorVisualMode: string;
|
|
83
89
|
hasChanges: boolean;
|
|
90
|
+
changeSeq: number;
|
|
84
91
|
isStripoInitialized: boolean;
|
|
85
92
|
templateId: string;
|
|
86
93
|
syncModulesEnabled: boolean;
|
|
@@ -94,6 +101,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
94
101
|
isPreviewModeOpen: boolean;
|
|
95
102
|
editorVisualMode: string;
|
|
96
103
|
hasChanges: boolean;
|
|
104
|
+
changeSeq: number;
|
|
97
105
|
isStripoInitialized: boolean;
|
|
98
106
|
templateId: string;
|
|
99
107
|
syncModulesEnabled: boolean;
|
|
@@ -106,6 +114,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
106
114
|
isPreviewModeOpen: boolean;
|
|
107
115
|
editorVisualMode: string;
|
|
108
116
|
hasChanges: boolean;
|
|
117
|
+
changeSeq: number;
|
|
109
118
|
isStripoInitialized: boolean;
|
|
110
119
|
templateId: string;
|
|
111
120
|
syncModulesEnabled: boolean;
|
|
@@ -119,6 +128,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
119
128
|
isPreviewModeOpen: boolean;
|
|
120
129
|
editorVisualMode: string;
|
|
121
130
|
hasChanges: boolean;
|
|
131
|
+
changeSeq: number;
|
|
122
132
|
isStripoInitialized: boolean;
|
|
123
133
|
templateId: string;
|
|
124
134
|
syncModulesEnabled: boolean;
|
|
@@ -131,6 +141,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
131
141
|
isPreviewModeOpen: boolean;
|
|
132
142
|
editorVisualMode: string;
|
|
133
143
|
hasChanges: boolean;
|
|
144
|
+
changeSeq: number;
|
|
134
145
|
isStripoInitialized: boolean;
|
|
135
146
|
templateId: string;
|
|
136
147
|
syncModulesEnabled: boolean;
|
|
@@ -144,6 +155,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
144
155
|
isPreviewModeOpen: boolean;
|
|
145
156
|
editorVisualMode: string;
|
|
146
157
|
hasChanges: boolean;
|
|
158
|
+
changeSeq: number;
|
|
147
159
|
isStripoInitialized: boolean;
|
|
148
160
|
templateId: string;
|
|
149
161
|
syncModulesEnabled: boolean;
|
|
@@ -156,6 +168,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
156
168
|
isPreviewModeOpen: boolean;
|
|
157
169
|
editorVisualMode: string;
|
|
158
170
|
hasChanges: boolean;
|
|
171
|
+
changeSeq: number;
|
|
159
172
|
isStripoInitialized: boolean;
|
|
160
173
|
templateId: string;
|
|
161
174
|
syncModulesEnabled: boolean;
|
|
@@ -169,6 +182,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
169
182
|
isPreviewModeOpen: boolean;
|
|
170
183
|
editorVisualMode: string;
|
|
171
184
|
hasChanges: boolean;
|
|
185
|
+
changeSeq: number;
|
|
172
186
|
isStripoInitialized: boolean;
|
|
173
187
|
templateId: string;
|
|
174
188
|
syncModulesEnabled: boolean;
|
|
@@ -181,6 +195,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
181
195
|
isPreviewModeOpen: boolean;
|
|
182
196
|
editorVisualMode: string;
|
|
183
197
|
hasChanges: boolean;
|
|
198
|
+
changeSeq: number;
|
|
184
199
|
isStripoInitialized: boolean;
|
|
185
200
|
templateId: string;
|
|
186
201
|
syncModulesEnabled: boolean;
|
|
@@ -194,6 +209,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
194
209
|
isPreviewModeOpen: boolean;
|
|
195
210
|
editorVisualMode: string;
|
|
196
211
|
hasChanges: boolean;
|
|
212
|
+
changeSeq: number;
|
|
197
213
|
isStripoInitialized: boolean;
|
|
198
214
|
templateId: string;
|
|
199
215
|
syncModulesEnabled: boolean;
|
|
@@ -206,6 +222,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
206
222
|
isPreviewModeOpen: boolean;
|
|
207
223
|
editorVisualMode: string;
|
|
208
224
|
hasChanges: boolean;
|
|
225
|
+
changeSeq: number;
|
|
209
226
|
isStripoInitialized: boolean;
|
|
210
227
|
templateId: string;
|
|
211
228
|
syncModulesEnabled: boolean;
|
|
@@ -219,6 +236,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
219
236
|
isPreviewModeOpen: boolean;
|
|
220
237
|
editorVisualMode: string;
|
|
221
238
|
hasChanges: boolean;
|
|
239
|
+
changeSeq: number;
|
|
222
240
|
isStripoInitialized: boolean;
|
|
223
241
|
templateId: string;
|
|
224
242
|
syncModulesEnabled: boolean;
|
|
@@ -231,6 +249,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
231
249
|
isPreviewModeOpen: boolean;
|
|
232
250
|
editorVisualMode: string;
|
|
233
251
|
hasChanges: boolean;
|
|
252
|
+
changeSeq: number;
|
|
234
253
|
isStripoInitialized: boolean;
|
|
235
254
|
templateId: string;
|
|
236
255
|
syncModulesEnabled: boolean;
|
|
@@ -244,6 +263,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
244
263
|
isPreviewModeOpen: boolean;
|
|
245
264
|
editorVisualMode: string;
|
|
246
265
|
hasChanges: boolean;
|
|
266
|
+
changeSeq: number;
|
|
247
267
|
isStripoInitialized: boolean;
|
|
248
268
|
templateId: string;
|
|
249
269
|
syncModulesEnabled: boolean;
|
|
@@ -256,6 +276,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
256
276
|
isPreviewModeOpen: boolean;
|
|
257
277
|
editorVisualMode: string;
|
|
258
278
|
hasChanges: boolean;
|
|
279
|
+
changeSeq: number;
|
|
259
280
|
isStripoInitialized: boolean;
|
|
260
281
|
templateId: string;
|
|
261
282
|
syncModulesEnabled: boolean;
|
|
@@ -269,6 +290,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
269
290
|
isPreviewModeOpen: boolean;
|
|
270
291
|
editorVisualMode: string;
|
|
271
292
|
hasChanges: boolean;
|
|
293
|
+
changeSeq: number;
|
|
272
294
|
isStripoInitialized: boolean;
|
|
273
295
|
templateId: string;
|
|
274
296
|
syncModulesEnabled: boolean;
|
|
@@ -281,6 +303,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
281
303
|
isPreviewModeOpen: boolean;
|
|
282
304
|
editorVisualMode: string;
|
|
283
305
|
hasChanges: boolean;
|
|
306
|
+
changeSeq: number;
|
|
284
307
|
isStripoInitialized: boolean;
|
|
285
308
|
templateId: string;
|
|
286
309
|
syncModulesEnabled: boolean;
|
|
@@ -294,6 +317,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
294
317
|
isPreviewModeOpen: boolean;
|
|
295
318
|
editorVisualMode: string;
|
|
296
319
|
hasChanges: boolean;
|
|
320
|
+
changeSeq: number;
|
|
297
321
|
isStripoInitialized: boolean;
|
|
298
322
|
templateId: string;
|
|
299
323
|
syncModulesEnabled: boolean;
|
|
@@ -306,6 +330,7 @@ export declare const useEditorStore: import("pinia").StoreDefinition<"guidoEdito
|
|
|
306
330
|
isPreviewModeOpen: boolean;
|
|
307
331
|
editorVisualMode: string;
|
|
308
332
|
hasChanges: boolean;
|
|
333
|
+
changeSeq: number;
|
|
309
334
|
isStripoInitialized: boolean;
|
|
310
335
|
templateId: string;
|
|
311
336
|
syncModulesEnabled: boolean;
|
|
@@ -1,41 +1,63 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* store carries the contract Guido is willing to share.
|
|
14
|
-
*/
|
|
15
|
-
export declare const useGuidoEmailEditorStore: import("pinia").StoreDefinition<"guidoEmailEditor", {
|
|
1
|
+
/** Terminal outcome of an editor write, keyed by `${blueprintId}:${version}`. */
|
|
2
|
+
export interface ApplyAck {
|
|
3
|
+
status: 'applied' | 'failed';
|
|
4
|
+
/** The `applyRequestedAt` of the click this ack answers — lets cortex drop stale acks. */
|
|
5
|
+
requestedAt: number;
|
|
6
|
+
/** Strictly increasing per key, so two acks in the same millisecond still order. */
|
|
7
|
+
ackAt: number;
|
|
8
|
+
/** Stringified — cortex JSON-clones store-derived payloads, an Error would not survive. */
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
/** The published editor snapshot — the shape a consumer reads off the shared store. */
|
|
12
|
+
export interface GuidoEmailEditorState {
|
|
16
13
|
/** Current Stripo HTML — stripe tables only, no DOCTYPE/wrapper. */
|
|
17
14
|
html: string;
|
|
18
15
|
/** Current Stripo CSS — es-p* utility classes used by the html. */
|
|
19
16
|
css: string;
|
|
20
17
|
/** Wall-clock ms when html/css were last written by the bridge. */
|
|
21
18
|
lastUpdatedAt: number;
|
|
22
|
-
/** Template id from the editor config,
|
|
19
|
+
/** Template id from the editor config, so a consumer can scope per-template. */
|
|
23
20
|
templateId: string;
|
|
24
|
-
|
|
21
|
+
/** True while a mounted Guido bridge is consuming the chat store. Gates cortex's Apply button. */
|
|
22
|
+
bridgeActive: boolean;
|
|
23
|
+
/** `${blueprintId}:${version}` → terminal outcome of the editor write, written only after it settles. */
|
|
24
|
+
applyAcks: Record<string, ApplyAck>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Outbound contract Guido publishes to the host's shared Pinia singleton so a
|
|
28
|
+
* consumer can read the current editor state: cortex-fe forwards it as
|
|
29
|
+
* `clientState.editor` on the next chat-stream POST, email-fe scans it for the
|
|
30
|
+
* Array of Objects lock.
|
|
31
|
+
*
|
|
32
|
+
* Published as `@useinsider/guido/editor-state` and from the root barrel, so a
|
|
33
|
+
* consumer imports this store instead of re-declaring one under the same id.
|
|
34
|
+
*
|
|
35
|
+
* This store is **state-only, by contract**. It declares no actions and no
|
|
36
|
+
* non-trivial getters, so `pinia.state.value['guidoEmailEditor']` is a complete
|
|
37
|
+
* view of it. cortex-fe reads it through that public path; adding an action or
|
|
38
|
+
* a getter that cortex needs would silently break that read. Keep it
|
|
39
|
+
* state-only. (`hasSnapshot` stays — cortex does not read it.)
|
|
40
|
+
*
|
|
41
|
+
* `useGuidoStateBridge` (the snapshot) and `useCortexBlueprintBridge` (the
|
|
42
|
+
* apply handshake) are the only intended writers, but that is Guido's own
|
|
43
|
+
* convention rather than something the store enforces: publishing the
|
|
44
|
+
* definition hands a consumer `$patch` as well, exactly as the runtime
|
|
45
|
+
* `pinia._s` probe always did.
|
|
46
|
+
*
|
|
47
|
+
* Store id `guidoEmailEditor` is cross-MFE: cortex-fe reads it at runtime rather
|
|
48
|
+
* than importing it, so the id stays part of the contract. The internal Guido
|
|
49
|
+
* editor store keeps id `guidoEditor` — these are intentionally separate. The
|
|
50
|
+
* internal store carries UI state a consumer should not see; this public store
|
|
51
|
+
* carries the contract Guido is willing to share.
|
|
52
|
+
*/
|
|
53
|
+
export declare const useGuidoEmailEditorStore: import("pinia").StoreDefinition<"guidoEmailEditor", GuidoEmailEditorState, {
|
|
25
54
|
/** True once the bridge has published at least one snapshot. */
|
|
26
55
|
hasSnapshot: (state: {
|
|
27
56
|
html: string;
|
|
28
57
|
css: string;
|
|
29
58
|
lastUpdatedAt: number;
|
|
30
59
|
templateId: string;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/** Current Stripo CSS — es-p* utility classes used by the html. */
|
|
35
|
-
css: string;
|
|
36
|
-
/** Wall-clock ms when html/css were last written by the bridge. */
|
|
37
|
-
lastUpdatedAt: number;
|
|
38
|
-
/** Template id from the editor config, for cortex-fe to scope per-template. */
|
|
39
|
-
templateId: string;
|
|
40
|
-
}>) => boolean;
|
|
60
|
+
bridgeActive: boolean;
|
|
61
|
+
applyAcks: Record<string, ApplyAck>;
|
|
62
|
+
} & import("pinia").PiniaCustomStateProperties<GuidoEmailEditorState>) => boolean;
|
|
41
63
|
}, {}>;
|
|
@@ -30,3 +30,9 @@ export declare const replaceString: (string?: string, values?: Record<string, st
|
|
|
30
30
|
* a string nor a number becomes ''.
|
|
31
31
|
*/
|
|
32
32
|
export declare const asText: (value: unknown) => string;
|
|
33
|
+
/**
|
|
34
|
+
* Narrows an unknown API value to a plain JSON object. Arrays and exotic
|
|
35
|
+
* objects (Blob, Date, FormData) are rejected, so a response body that useHttp
|
|
36
|
+
* parsed as text or blob can never be read as a JSON payload.
|
|
37
|
+
*/
|
|
38
|
+
export declare const isPlainObject: <T>(value: T) => value is T & Record<string, unknown>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BaseDynamicContent } from '@@/Types/generic';
|
|
2
|
+
export type DynamicContentFormat = NonNullable<BaseDynamicContent['format']>;
|
|
3
|
+
/** Format key the dynamic-content picker sends for a custom date/time format. */
|
|
4
|
+
export declare const TIME_FORMAT_KEY = "time-format";
|
|
5
|
+
/** Wraps a Liquid filter argument in whichever quote style the value itself does not use. */
|
|
6
|
+
export declare const quoteArgument: (value: string) => string | null;
|
|
7
|
+
/**
|
|
8
|
+
* `{ key: 'time-format', value: '%Y-%m-%d' }` -> `date: "%Y-%m-%d"`.
|
|
9
|
+
*
|
|
10
|
+
* Only a custom date/time strftime pattern has a Liquid equivalent. Anything else returns null so
|
|
11
|
+
* the caller emits a plain tag rather than one that silently lost — or literally printed — its
|
|
12
|
+
* format.
|
|
13
|
+
* @example formatToLiquidFilter({ key: 'time-format', value: '%b %-d, %Y' }) -> date: "%b %-d, %Y"
|
|
14
|
+
*/
|
|
15
|
+
export declare const formatToLiquidFilter: (format: DynamicContentFormat) => string | null;
|
|
16
|
+
/**
|
|
17
|
+
* Inverse of `formatToLiquidFilter` — reads the picker format back out of one filter segment, so a
|
|
18
|
+
* reopened tag restores its selection in the host's format dropdown.
|
|
19
|
+
*/
|
|
20
|
+
export declare const liquidFilterToFormat: (segment: string) => DynamicContentFormat | null;
|
package/dist/stores/editor.js
CHANGED
|
@@ -8,6 +8,10 @@ const o = e("guidoEditor", {
|
|
|
8
8
|
isPreviewModeOpen: !1,
|
|
9
9
|
editorVisualMode: "desktop",
|
|
10
10
|
hasChanges: !1,
|
|
11
|
+
// Bumped on every Stripo document change. `hasChanges` is a latch that stays
|
|
12
|
+
// true until a save, so it cannot signal the second edit onwards; watchers
|
|
13
|
+
// that need every change (the outbound state bridge) watch this instead.
|
|
14
|
+
changeSeq: 0,
|
|
11
15
|
isStripoInitialized: !1,
|
|
12
16
|
templateId: "",
|
|
13
17
|
syncModulesEnabled: !1,
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { defineStore as e } from "pinia";
|
|
2
2
|
const d = e("guidoEmailEditor", {
|
|
3
3
|
state: () => ({
|
|
4
|
-
/** Current Stripo HTML — stripe tables only, no DOCTYPE/wrapper. */
|
|
5
4
|
html: "",
|
|
6
|
-
/** Current Stripo CSS — es-p* utility classes used by the html. */
|
|
7
5
|
css: "",
|
|
8
|
-
/** Wall-clock ms when html/css were last written by the bridge. */
|
|
9
6
|
lastUpdatedAt: 0,
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
templateId: "",
|
|
8
|
+
bridgeActive: !1,
|
|
9
|
+
applyAcks: {}
|
|
12
10
|
}),
|
|
13
11
|
getters: {
|
|
14
12
|
/** True once the bridge has published at least one snapshot. */
|