@vectoriox/iox-ui 5.47.0 → 5.48.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/design-core/index.d.ts +5 -3
- package/fesm2022/vectoriox-iox-ui-design-core.mjs +7 -4
- package/fesm2022/vectoriox-iox-ui-design-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-generation-core.mjs +128 -1
- package/fesm2022/vectoriox-iox-ui-generation-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-harness-core.mjs +71 -1
- package/fesm2022/vectoriox-iox-ui-harness-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui.mjs +29 -1
- package/fesm2022/vectoriox-iox-ui.mjs.map +1 -1
- package/generation-core/index.d.ts +109 -3
- package/harness-core/index.d.ts +21 -2
- package/index.d.ts +22 -3
- package/package.json +1 -1
- package/src/design-core/design-apply.ts +8 -4
- package/src/generation-core/entrances.ts +110 -0
- package/src/generation-core/plan-data.ts +71 -0
- package/src/generation-core/public-api.ts +2 -0
- package/src/harness-core/intake.ts +85 -0
- package/src/harness-core/public-api.ts +1 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DesignChoices } from '@vectoriox/iox-ui/design-core';
|
|
1
|
+
import { DesignChoices, AnimateId } from '@vectoriox/iox-ui/design-core';
|
|
2
2
|
|
|
3
3
|
declare const SPEC_SITE_PLAN_SHAPE: {
|
|
4
4
|
readonly plan: {
|
|
@@ -2709,5 +2709,111 @@ declare function removeBarItem(globals: ReadonlyArray<SerializedNode>, itemId: s
|
|
|
2709
2709
|
/** The bar's Items list drag-to-reorder (board 2): item `from` moves to `to`. Input not mutated. */
|
|
2710
2710
|
declare function moveBarItem(globals: ReadonlyArray<SerializedNode>, from: number, to: number): BarItemsEditResult;
|
|
2711
2711
|
|
|
2712
|
-
|
|
2713
|
-
|
|
2712
|
+
/**
|
|
2713
|
+
* How animated (AI plan rev 3.1 §3p) — the ONE reading of the contract's `axes.animations` (what each level
|
|
2714
|
+
* moves, how) and `axes.animationFamilies` (which parts a section family lets move). The pieces every stamper
|
|
2715
|
+
* shares: the Design step's business specimen (H4-4) today, `planEntrances` in the composer (M1-6b) next. Pure.
|
|
2716
|
+
*
|
|
2717
|
+
* A level with `content` moves a section's content together, once; otherwise it names parts (heading, items,
|
|
2718
|
+
* media…) and each part enters on its own, items one after another. Navigation, footers and globals are a family
|
|
2719
|
+
* with no parts, so nothing there ever moves.
|
|
2720
|
+
*/
|
|
2721
|
+
declare enum EntrancePart {
|
|
2722
|
+
Content = "content",
|
|
2723
|
+
Heading = "heading",
|
|
2724
|
+
Items = "items",
|
|
2725
|
+
Media = "media",
|
|
2726
|
+
RowMedia = "rowMedia",
|
|
2727
|
+
HeroText = "heroText"
|
|
2728
|
+
}
|
|
2729
|
+
/** What an entrance stamp is marked with — a later restamp touches only what the look made (H4-5). */
|
|
2730
|
+
declare const ENTRANCE_ORIGIN_LOOK = "look";
|
|
2731
|
+
/** The trigger every entrance plays on. */
|
|
2732
|
+
declare const ENTRANCE_TRIGGER = "viewportEnter";
|
|
2733
|
+
/** The easing every entrance plays with (only `opacity` and `transform` move). */
|
|
2734
|
+
declare const ENTRANCE_EASING = "ease-out";
|
|
2735
|
+
/** One entrance as the contract states it. */
|
|
2736
|
+
interface EntranceSpec {
|
|
2737
|
+
readonly type: string;
|
|
2738
|
+
readonly duration: number;
|
|
2739
|
+
readonly distance?: number;
|
|
2740
|
+
/** Items: ms added per item, up to `cap` items. */
|
|
2741
|
+
readonly stagger?: number;
|
|
2742
|
+
readonly cap?: number;
|
|
2743
|
+
/** The type a right-to-left page plays instead (a side-by-side row slides in from ITS side). */
|
|
2744
|
+
readonly mirror?: string;
|
|
2745
|
+
}
|
|
2746
|
+
/** What one section family moves at one level. `content` set = the whole content together, and no parts. */
|
|
2747
|
+
interface SectionEntrances {
|
|
2748
|
+
readonly content: EntranceSpec | null;
|
|
2749
|
+
readonly parts: Readonly<Partial<Record<EntrancePart, EntranceSpec>>>;
|
|
2750
|
+
}
|
|
2751
|
+
/** The interaction action a stamp carries — the shape both interaction runtimes play. */
|
|
2752
|
+
interface EntranceAction {
|
|
2753
|
+
type: string;
|
|
2754
|
+
target: 'self';
|
|
2755
|
+
duration: number;
|
|
2756
|
+
delay: number;
|
|
2757
|
+
easing: typeof ENTRANCE_EASING;
|
|
2758
|
+
once: true;
|
|
2759
|
+
params?: {
|
|
2760
|
+
distance: number;
|
|
2761
|
+
};
|
|
2762
|
+
}
|
|
2763
|
+
interface EntranceInteraction {
|
|
2764
|
+
id: string;
|
|
2765
|
+
trigger: typeof ENTRANCE_TRIGGER;
|
|
2766
|
+
actions: [EntranceAction];
|
|
2767
|
+
origin: typeof ENTRANCE_ORIGIN_LOOK;
|
|
2768
|
+
}
|
|
2769
|
+
/** What a section of `family` moves at `level` — nothing for an unknown level or family. */
|
|
2770
|
+
declare function sectionEntrances(level: AnimateId | string | null | undefined, family: string): SectionEntrances;
|
|
2771
|
+
/** The literal delay of the `index`-th item: `stagger` ms each, the last `cap` waiting no longer than the one before. */
|
|
2772
|
+
declare function staggerDelay(spec: EntranceSpec, index?: number): number;
|
|
2773
|
+
/** The action a node plays: the spec's type (mirrored on a right-to-left page), its distance and its delay. */
|
|
2774
|
+
declare function entranceAction(spec: EntranceSpec, opts?: {
|
|
2775
|
+
index?: number;
|
|
2776
|
+
rtl?: boolean;
|
|
2777
|
+
}): EntranceAction;
|
|
2778
|
+
/** The stamp on a node: `viewportEnter`, once, on itself (a duplicated node needs no remap), from the look. */
|
|
2779
|
+
declare function entranceInteraction(nodeId: string, action: EntranceAction): EntranceInteraction;
|
|
2780
|
+
|
|
2781
|
+
/**
|
|
2782
|
+
* What each page of a plan shows and where its data comes from (AI plan rev 3 §3d, `describePlanData`) — the
|
|
2783
|
+
* Plan step's data lines, in the vocabulary the builder already uses. Pure and STRUCTURED: the screen words
|
|
2784
|
+
* each kind in its own language ("Lists every Project", "One Project, found by its address /work/:slug").
|
|
2785
|
+
*/
|
|
2786
|
+
declare enum PageDataKind {
|
|
2787
|
+
/** A section repeats every item of a content type. */
|
|
2788
|
+
List = "list",
|
|
2789
|
+
/** A section repeats the newest `count` items. */
|
|
2790
|
+
Latest = "latest",
|
|
2791
|
+
/** A template page: one item, found by its address — one page per item. */
|
|
2792
|
+
One = "one",
|
|
2793
|
+
/** The next (or previous) item beside the page's own. */
|
|
2794
|
+
Siblings = "siblings",
|
|
2795
|
+
/** A form: what it sends, to the studio's inbox (+ an email when Email is connected). */
|
|
2796
|
+
Form = "form",
|
|
2797
|
+
/** Words and pictures, no data. */
|
|
2798
|
+
Static = "static"
|
|
2799
|
+
}
|
|
2800
|
+
interface PageDataLine {
|
|
2801
|
+
kind: PageDataKind;
|
|
2802
|
+
/** The content type's label ("Projects"), for every kind that reads one. */
|
|
2803
|
+
contentType?: string;
|
|
2804
|
+
/** Latest: how many. */
|
|
2805
|
+
count?: number;
|
|
2806
|
+
/** One: the page's own address, with its parameter. */
|
|
2807
|
+
route?: string;
|
|
2808
|
+
/** Form: the form preset (`contact`). */
|
|
2809
|
+
form?: string;
|
|
2810
|
+
}
|
|
2811
|
+
interface PageData {
|
|
2812
|
+
pageKey: string;
|
|
2813
|
+
lines: PageDataLine[];
|
|
2814
|
+
}
|
|
2815
|
+
/** One entry per page of the plan, in plan order. */
|
|
2816
|
+
declare function describePlanData(plan: SitePlan): PageData[];
|
|
2817
|
+
|
|
2818
|
+
export { BAR_ITEMS_MAX, BAR_ITEMS_MIN, BASELINE_ROLES, BaselineRole, BindScope, CardBindTarget, ComposeError, ComposeIssueCode, DANGLING_LINK_CODES, DEFAULT_PHONE_MENU_BREAKPOINT, ENTRANCE_EASING, ENTRANCE_ORIGIN_LOOK, ENTRANCE_TRIGGER, EntrancePart, FACT_KEYS, GlobalSlot, NAV_PART_LABELS, NAV_SHAPE_COPY, NEUTRAL_STYLE_PROPS, NODE_BASELINES, NavPagesSource, NavRole, NavShape, NavShapeError, PRICING_PATTERN, PUBLISH_BLOCKING_PLACEHOLDERS, PageDataKind, PathIssueCollector, PatternDirective, PatternGlobalPosition, PatternOpens, PatternPlaceholder, PatternRecipe, PhoneMenuBreakpoint, PlaceholderKind, PlanDataSourceMode, PlanDataSourceType, RelativeOp, SAMPLE_IMAGE_REF, SITE_FACT_LABELS, SITE_HEADER_PATTERN, SITE_PATTERNS, SITE_PLAN_VERSION, SITE_TEMPLATES, SPEC_SITE_FACTS, SPEC_SITE_LIBRARY, SPEC_SITE_PATTERNS, SPEC_SITE_PLAN_SHAPE, SPEC_SITE_TEMPLATES, STUDIO_TEMPLATE, SitePlanError, SitePlanIssueCode, SitePlanSourceKind, SiteTemplateError, SiteTemplateId, addBarItem, altSourceScopes, assertStyleSlots, assertValidSitePlan, barIconForRoute, blocksPublish, composeNavHeader, composeSection, composeSite, createSerializedNode, describePlanData, entranceAction, entranceInteraction, findNavRoot, isNonEmptyString, isPlainObject, moveBarItem, navPages, navShapeBreakpoint, navShapeOf, nextSourceOf, nodeBindPaths, parseSampleImageRef, pathAt, patternById, patternNeedsBinding, patternNodes, patternOptionalParts, patternScopes, patternSlots, patternTakesBinding, phoneMenuPages, planFromTemplate, planNavShape, releasedLayoutDefaults, removeBarItem, requiredShapeKeys, routeParams, sampleImageRef, sectionEntrances, setStyleProps, shapeKeys, siteNavPages, staggerDelay, validateSitePlan, walkSerialized };
|
|
2819
|
+
export type { AddBarItemResult, BarItemsEditResult, CmsDataSourcePlan, ComposeContext, ComposeIssue, ComposedDataSource, ComposedItem, ComposedNavHeader, ComposedPage, ComposedPlaceholder, ComposedRoute, ComposedSection, ComposedSite, ContentFieldPlan, ContentSeedPlan, ContentTypePlan, CreateNodeOptions, DataSourcePlan, EntranceAction, EntranceInteraction, EntranceSpec, FactKey, NavHeaderInput, NavHeaderOptions, NavPage, NavShapeOptions, NavShapeResult, NodeBaseline, PageData, PageDataLine, PagePlan, PageSectionPlan, PathIssue, PatternNode, PlainObject, RelativeDataSourcePlan, SectionEntrances, SectionInput, SectionOptions, SerializedBinding, SerializedNode, SerializedResponsive, ShapeEntry, ShapeIssueCodes, SiteBusiness, SitePageRef, SitePattern, SitePlan, SitePlanIssue, SitePlanSource, SitePlanValidation, SiteTemplate, TemplatePlanInput };
|
package/harness-core/index.d.ts
CHANGED
|
@@ -2475,5 +2475,24 @@ interface PromptContextOptions {
|
|
|
2475
2475
|
}
|
|
2476
2476
|
declare function analysisPromptContext(analysis: SiteAnalysis, opts?: PromptContextOptions): PromptContext;
|
|
2477
2477
|
|
|
2478
|
-
|
|
2479
|
-
|
|
2478
|
+
interface BusinessTypeMatch {
|
|
2479
|
+
id: string;
|
|
2480
|
+
/** The keywords found, in the type's order. */
|
|
2481
|
+
keywords: string[];
|
|
2482
|
+
/** The letters matched — longer, more specific keywords weigh more ("barber" over "bar"). */
|
|
2483
|
+
score: number;
|
|
2484
|
+
}
|
|
2485
|
+
/** The kind of business the words name, best first; empty when none of the keywords appears. Ties keep the board's order. */
|
|
2486
|
+
declare function matchBusinessTypes(text: string, types?: ReadonlyArray<BusinessType>): BusinessTypeMatch[];
|
|
2487
|
+
/** The single best kind of business the words name, or null. */
|
|
2488
|
+
declare function matchBusinessType(text: string, types?: ReadonlyArray<BusinessType>): string | null;
|
|
2489
|
+
/** An address as a chip shows it: no scheme, no `www.`, no trailing punctuation or slash, lower-case host. */
|
|
2490
|
+
declare function normalizeAddress(raw: string): string;
|
|
2491
|
+
/** Every website address typed or pasted in the words (an http(s) address or a bare domain), once each, in
|
|
2492
|
+
* order. An email address is not a website. */
|
|
2493
|
+
declare function addressesIn(text: string): string[];
|
|
2494
|
+
/** The words without one address (removing its chip): every spelling of it, and the space it leaves. */
|
|
2495
|
+
declare function withoutAddress(text: string, address: string): string;
|
|
2496
|
+
|
|
2497
|
+
export { ANIMATE_ADVICE_STEPS, AnalysisIssueCode, AnalysisLocale, AvoidCheck, BUSINESS_TYPES, BUSINESS_TYPE_IDS, BookingNeed, COPY_LENGTH_ORDER, CopyLength, HARNESS_SOURCE, LITTLE_MOVEMENT_AVOID, LITTLE_MOVEMENT_CAP, LintKind, MAX_ANALYSIS_MOODS, NEEDS, NEED_IDS, NeedSource, PresetFit, RANK_WEIGHTS, READ_ANIMATE, READ_WEIGHTS, RankReason, SPEC_ANALYSIS_SHAPE, SPEC_BUSINESS_TYPES, SPEC_HARNESS_EXAMPLES, SPEC_HARNESS_SOURCE, SPEC_NEEDS, SPEC_STRUCTURES, SPEC_USER_AVOIDS, STRUCTURES, StyleReadGround, StyleReadHeadings, StyleReadMotion, StyleReadSourceKind, USER_AVOID_IDS, VoiceFormality, VoicePerson, addressesIn, analysisPromptContext, businessTypeById, calmerChoice, inferNeeds, isLookRule, lintDesign, lookMatches, mapStyleRead, matchBusinessType, matchBusinessTypes, motionFor, normalizeAddress, oklchHue, presetHasSerifHeadings, presetLook, rankStylePresets, rankWithReferences, userAvoidRule, validateSiteAnalysis, withoutAddress };
|
|
2498
|
+
export type { AnalysisContext, AnalysisIssue, AnalysisValidation, AvoidRule, BusinessType, BusinessTypeMatch, CheckRule, FactRead, GoodFit, LintFinding, LintSite, LookCondition, LookDesign, LookRule, MotionInput, MotionStart, NeedDef, NeedId, PromptContext, PromptContextOptions, PromptSection, RankInput, RankedPreset, SiteAnalysis, SiteNeed, StructureId, StyleHints, StyleRead, UserAvoidId };
|
package/index.d.ts
CHANGED
|
@@ -113,13 +113,15 @@ interface PageScrollStart {
|
|
|
113
113
|
* Scrolling choice (`planScroll`); `scrollTo` actions and the dialog lock go through it whether or not
|
|
114
114
|
* Lenis runs. Never on the server: there is no window to scroll.
|
|
115
115
|
*/
|
|
116
|
-
declare class PageScrollProvider {
|
|
116
|
+
declare class PageScrollProvider implements OnDestroy {
|
|
117
117
|
private zone;
|
|
118
118
|
private lenis;
|
|
119
119
|
private rafId?;
|
|
120
120
|
private wrapper;
|
|
121
121
|
private lockedOverflow;
|
|
122
122
|
init(start: PageScrollStart): void;
|
|
123
|
+
/** Provided by a component (a page, the Design step's specimen): its Lenis and loop go with it. */
|
|
124
|
+
ngOnDestroy(): void;
|
|
123
125
|
/** Stop Lenis and its loop; the page falls back to the browser's own scrolling. */
|
|
124
126
|
destroy(): void;
|
|
125
127
|
/** Hard-stop page scroll (dialog open, A1b): wheel/touch frozen until start(). */
|
|
@@ -3034,6 +3036,23 @@ declare function hasOwnPageTransition(anim: RouteAnimationLike | null | undefine
|
|
|
3034
3036
|
*/
|
|
3035
3037
|
declare function pageTransitionOf(anim: RouteAnimationLike | null | undefined, ctx?: PageTransitionContext): PlannedPageTransition;
|
|
3036
3038
|
|
|
3039
|
+
/** The two layers a builder preview of a page change plays on, and the stage that holds them. */
|
|
3040
|
+
interface TransitionPreviewLayers {
|
|
3041
|
+
/** Holds both layers; it carries the perspective the 3D presets need. */
|
|
3042
|
+
stage: HTMLElement;
|
|
3043
|
+
/** The page that leaves. */
|
|
3044
|
+
out: HTMLElement | null;
|
|
3045
|
+
/** The page that arrives. */
|
|
3046
|
+
in: HTMLElement | null;
|
|
3047
|
+
}
|
|
3048
|
+
/**
|
|
3049
|
+
* Play a resolved page change (`resolvePageTransition`) on two stacked layers — the ONE dual-layer preview the
|
|
3050
|
+
* builder's Page panel and the Design step's specimen both play (rev 3.1 §3o). The live site plays the same
|
|
3051
|
+
* frames through its own transition service. Resolves when the arriving layer has finished (or at once when
|
|
3052
|
+
* there is no arriving layer). The layers are left at their last frame: the caller decides what to show after.
|
|
3053
|
+
*/
|
|
3054
|
+
declare function playDualLayerTransition(resolved: ResolvedPageTransition, layers: TransitionPreviewLayers): Promise<void>;
|
|
3055
|
+
|
|
3037
3056
|
/**
|
|
3038
3057
|
* Gallery effect registry — the SHARED, effect-agnostic description of how each gallery slide looks
|
|
3039
3058
|
* and animates in a given crossfade state. Modeled on `binding-pipes.ts` / `PAGE_TRANSITION_PRESETS`:
|
|
@@ -3156,5 +3175,5 @@ declare class GalleryLoop {
|
|
|
3156
3175
|
private emit;
|
|
3157
3176
|
}
|
|
3158
3177
|
|
|
3159
|
-
export { A11yAttribute, A11yTrait, ACCESSIBLE_NAME_TYPES, AOSService, ARIA_CURRENT_PAGE, BACKDROP_TWIN_KEY, BACKDROP_WHOLE_KEY, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, COMPONENT_STATE_SELECTOR, CONTAINER_HTML_TAGS, CONTEXT_ACTION_TYPES, CURRENT_LINK_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ComponentState, ContainerComponent, ContextStore, DECORATIVE_TYPES, DEFAULT_GALLERY_SCHEDULER, DEFAULT_PAGE_TRANSITION, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_EASING, DEFAULT_PAGE_TRANSITION_PERSPECTIVE, DEFAULT_PHOSPHOR_WEIGHT, DEFAULT_WIDTH_BY_TYPE, DIALOG_CLOSE_ABOVE_ATTRIBUTE, DIALOG_NODE_TYPE, DeclarationContext, ENTRANCE_ACTION_TYPES, EXIT_ACTION_TYPES, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, HOVER_DEFAULT_TRANSITION, HOVER_STATES, INTERACTION_HIDDEN_ATTRIBUTE, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxDialogComponent, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, IoxVideoComponent, LAST_EMITTED_STATES, LEGACY_DIRECTION_KEY, LEGACY_HOVER_TRANSITION, LEGACY_SWIPER_CONTROLS, LinkedContainerComponent, MANAGED_A11Y_ATTRIBUTES, MAX_LINES_KEY, NAMED_GROUP_ROLES, NO_PAGE_TRANSITION, ON_STATE_ROLES, PAGE_STYLE_TOKENS, PAGE_TOKEN_FALLBACKS, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PART_PAGINATION_MODIFIER, PHONE_TRANSITION_QUERY, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PageStyleField, PageStyleSource, REDUCED_MOTION_QUERY, SCROLL_EASINGS, SLIDER_INPUTS_ATTRIBUTE, SLIDER_INPUT_NAMES, STATE_PSEUDO_CLASS, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_A11Y_OPTIONS, SWIPER_RESPONSIVE_TRAITS, SWIPER_TRIGGER_EVENTS, ScrollEasing, SectionComponent, SwiperModuleName, TARGETED_STATE_SUFFIX, TEXT_ALIGN_KEY, TEXT_DIRECTION_KEY, TRANSITION_FEELS, TextAlign, TextBlockComponent, VALIDATION_STATES, VALIDATION_STATE_CLASS, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, VisibilityActionType, activeActions, applyBindingPipes, applyFeel, bootSlider, breakpointDef, breakpointForWidth, breakpointQueryFor, buildInteractionKeyframes, buildPartSelector, buildSwiperBreakpoints, buildTargetedStateSelector, buildTargetedSuffixSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, closeAboveMediaQuery, collectContextSlotKeys, compileDeclarations, compilePageStyle, componentStatesFor, composeVirtualTraits, containerHtmlTag, contextActionEffect, contextSlotKeys, countOverrides, declaredInputNames, decomposeBackdropFilter, defaultHoverTransition, defaultMessageResolver, deriveSeoFromContent, describePageStyle, describeSeoFields, dialogCloseAboveMinWidth, domHasSliderParts, effectiveActionType, effectiveTransitionId, entrancePreStateStyle, feelFor, finalKeyframe, getSeoValue, hasHoverState, hasOwnPageTransition, hasOwnTransition, initialSlotValues, isActionEnabled, isContextAction, isCurrentLink, isExternalOrFragment, isGradientPaint, isInputOverriddenAt, isLastEmittedStateKey, isOverriddenAt, isValidSlotKey, isWholeBackdropSet, keyframePropertyName, keyframeStyleEntries, legacyEnterToTransition, mediaQueryFor, mergeSwiperBreakpoints, migrateLegacyDirection, migrateLegacyTextAlign, motionAllowed, nearestCommonAncestorId, needsAccessibleName, needsGroupName, nodeA11yAttributes, nodeCssId, nodeTraitValue, nodeUsesContext, normalizeAppPath, normalizeGalleryImage, normalizeIconClass, orderStatesForEmit, pageTransitionOf, parentBreakpoint, parseUrlList, partClassName, planResponsiveRules, planScroll, planSwiperOptions, prefersReducedMotion, renderDefaultDeclarations, resetPageStyleField, resetSeoField, resolveBoundValue, resolveContextValue, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveSeo, resolveStateStyles, resolveStyleProps, resolveToggleVisibility, resolveValueChain, rewriteViewportUnits, safeSetInput, serializeA11yAttributes, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, sliderControlElements, sliderInputsFromProps, sliderInputsOf, stateNamesIn, statePseudoClass, stateSelectorSuffix, stripSuppressedProps, swiperAutoplayAllowed, swiperLoopOptions, swiperModuleNames, syncNodeA11yAttributes, targetedStateSelector, viewportTransitionContext, withContextSlots, withDefaultHoverTransition, wrapInBreakpointQuery, wrapInMediaQuery };
|
|
3160
|
-
export type { A11yNodeSource, BoundValueSpec, BreakpointDef, ContextActionEffect, ContextActionLike, ContextItemRef, ContextSlotDefault, ContextValueSpec, ContextWriteScope, CurrentLinkOptions, FormMessageResolver, GalleryBoundSource, GalleryDurations, GalleryEffect, GalleryEffectDescriptor, GalleryImage, GalleryLoopOptions, GalleryLoopScheduler, GalleryLoopState, GallerySlideState, IBuilderEventEmitter, InputMap, InteractionKeyframe, IoxContextSlot, IoxPipe, MatchMediaHost, NodeA11yContext, OverrideBreakpoint, PageScrollStart, PageStyleFieldState, PageStyleOptions, PageStyleSettings, PageTransitionCategory, PageTransitionCategoryMeta, PageTransitionContext, PageTransitionOptions, PageTransitionPreset, PipeArgSpec, PipeContext, PipeDescriptor, PlannedPageTransition, ResolvedPageTransition, ResponsiveLayer, ResponsiveOverrides, ResponsiveRuleSet, RouteAnimationLike, ScrollPlanContext, SeoDerivedContent, SeoFieldSource, SeoFieldState, SeoResolveContext, SetContextParams, SliderBootOptions, SliderControlElements, SliderInputs, StateStyleMap, StyleMap, StyleTreeNode, SwiperBreakpointMap, SwiperModuleFlags, SwiperPlan, SwiperPlanContext, ToggleableAction, TransitionFeel, TransitionPhase, TransitionTimelineInput, TransitionViewport, ValueSource, VirtualTraitGroup };
|
|
3178
|
+
export { A11yAttribute, A11yTrait, ACCESSIBLE_NAME_TYPES, AOSService, ARIA_CURRENT_PAGE, BACKDROP_TWIN_KEY, BACKDROP_WHOLE_KEY, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, COMPONENT_STATE_SELECTOR, CONTAINER_HTML_TAGS, CONTEXT_ACTION_TYPES, CURRENT_LINK_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ComponentState, ContainerComponent, ContextStore, DECORATIVE_TYPES, DEFAULT_GALLERY_SCHEDULER, DEFAULT_PAGE_TRANSITION, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_EASING, DEFAULT_PAGE_TRANSITION_PERSPECTIVE, DEFAULT_PHOSPHOR_WEIGHT, DEFAULT_WIDTH_BY_TYPE, DIALOG_CLOSE_ABOVE_ATTRIBUTE, DIALOG_NODE_TYPE, DeclarationContext, ENTRANCE_ACTION_TYPES, EXIT_ACTION_TYPES, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, HOVER_DEFAULT_TRANSITION, HOVER_STATES, INTERACTION_HIDDEN_ATTRIBUTE, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxDialogComponent, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, IoxVideoComponent, LAST_EMITTED_STATES, LEGACY_DIRECTION_KEY, LEGACY_HOVER_TRANSITION, LEGACY_SWIPER_CONTROLS, LinkedContainerComponent, MANAGED_A11Y_ATTRIBUTES, MAX_LINES_KEY, NAMED_GROUP_ROLES, NO_PAGE_TRANSITION, ON_STATE_ROLES, PAGE_STYLE_TOKENS, PAGE_TOKEN_FALLBACKS, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PART_PAGINATION_MODIFIER, PHONE_TRANSITION_QUERY, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PageStyleField, PageStyleSource, REDUCED_MOTION_QUERY, SCROLL_EASINGS, SLIDER_INPUTS_ATTRIBUTE, SLIDER_INPUT_NAMES, STATE_PSEUDO_CLASS, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_A11Y_OPTIONS, SWIPER_RESPONSIVE_TRAITS, SWIPER_TRIGGER_EVENTS, ScrollEasing, SectionComponent, SwiperModuleName, TARGETED_STATE_SUFFIX, TEXT_ALIGN_KEY, TEXT_DIRECTION_KEY, TRANSITION_FEELS, TextAlign, TextBlockComponent, VALIDATION_STATES, VALIDATION_STATE_CLASS, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, VisibilityActionType, activeActions, applyBindingPipes, applyFeel, bootSlider, breakpointDef, breakpointForWidth, breakpointQueryFor, buildInteractionKeyframes, buildPartSelector, buildSwiperBreakpoints, buildTargetedStateSelector, buildTargetedSuffixSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, closeAboveMediaQuery, collectContextSlotKeys, compileDeclarations, compilePageStyle, componentStatesFor, composeVirtualTraits, containerHtmlTag, contextActionEffect, contextSlotKeys, countOverrides, declaredInputNames, decomposeBackdropFilter, defaultHoverTransition, defaultMessageResolver, deriveSeoFromContent, describePageStyle, describeSeoFields, dialogCloseAboveMinWidth, domHasSliderParts, effectiveActionType, effectiveTransitionId, entrancePreStateStyle, feelFor, finalKeyframe, getSeoValue, hasHoverState, hasOwnPageTransition, hasOwnTransition, initialSlotValues, isActionEnabled, isContextAction, isCurrentLink, isExternalOrFragment, isGradientPaint, isInputOverriddenAt, isLastEmittedStateKey, isOverriddenAt, isValidSlotKey, isWholeBackdropSet, keyframePropertyName, keyframeStyleEntries, legacyEnterToTransition, mediaQueryFor, mergeSwiperBreakpoints, migrateLegacyDirection, migrateLegacyTextAlign, motionAllowed, nearestCommonAncestorId, needsAccessibleName, needsGroupName, nodeA11yAttributes, nodeCssId, nodeTraitValue, nodeUsesContext, normalizeAppPath, normalizeGalleryImage, normalizeIconClass, orderStatesForEmit, pageTransitionOf, parentBreakpoint, parseUrlList, partClassName, planResponsiveRules, planScroll, planSwiperOptions, playDualLayerTransition, prefersReducedMotion, renderDefaultDeclarations, resetPageStyleField, resetSeoField, resolveBoundValue, resolveContextValue, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveSeo, resolveStateStyles, resolveStyleProps, resolveToggleVisibility, resolveValueChain, rewriteViewportUnits, safeSetInput, serializeA11yAttributes, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, sliderControlElements, sliderInputsFromProps, sliderInputsOf, stateNamesIn, statePseudoClass, stateSelectorSuffix, stripSuppressedProps, swiperAutoplayAllowed, swiperLoopOptions, swiperModuleNames, syncNodeA11yAttributes, targetedStateSelector, viewportTransitionContext, withContextSlots, withDefaultHoverTransition, wrapInBreakpointQuery, wrapInMediaQuery };
|
|
3179
|
+
export type { A11yNodeSource, BoundValueSpec, BreakpointDef, ContextActionEffect, ContextActionLike, ContextItemRef, ContextSlotDefault, ContextValueSpec, ContextWriteScope, CurrentLinkOptions, FormMessageResolver, GalleryBoundSource, GalleryDurations, GalleryEffect, GalleryEffectDescriptor, GalleryImage, GalleryLoopOptions, GalleryLoopScheduler, GalleryLoopState, GallerySlideState, IBuilderEventEmitter, InputMap, InteractionKeyframe, IoxContextSlot, IoxPipe, MatchMediaHost, NodeA11yContext, OverrideBreakpoint, PageScrollStart, PageStyleFieldState, PageStyleOptions, PageStyleSettings, PageTransitionCategory, PageTransitionCategoryMeta, PageTransitionContext, PageTransitionOptions, PageTransitionPreset, PipeArgSpec, PipeContext, PipeDescriptor, PlannedPageTransition, ResolvedPageTransition, ResponsiveLayer, ResponsiveOverrides, ResponsiveRuleSet, RouteAnimationLike, ScrollPlanContext, SeoDerivedContent, SeoFieldSource, SeoFieldState, SeoResolveContext, SetContextParams, SliderBootOptions, SliderControlElements, SliderInputs, StateStyleMap, StyleMap, StyleTreeNode, SwiperBreakpointMap, SwiperModuleFlags, SwiperPlan, SwiperPlanContext, ToggleableAction, TransitionFeel, TransitionPhase, TransitionPreviewLayers, TransitionTimelineInput, TransitionViewport, ValueSource, VirtualTraitGroup };
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from './flavor-seed';
|
|
9
9
|
import { DesignChoices, DesignTokenRow, flavorSourceStamp, StoredDesignTokenRow } from './design-token.model';
|
|
10
10
|
import { FONT_FAMILY_TOKENS, fontFamilyKey, FontFamilyToken, fontRoleReferences, parseFontFamilies, withoutScriptPartners } from './fonts';
|
|
11
|
-
import { DesignConfigEntry, DesignConfigKey, designConfigEntries, flavorPackById, isRetiredFlavorId, packDesignChoices, StoredDesign } from './design-settings';
|
|
11
|
+
import { DesignConfigEntry, DesignConfigKey, designConfigEntries, flavorPackById, isRetiredFlavorId, packDesignChoices, sameDesignChoices, StoredDesign } from './design-settings';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Applying a look (design-system DS4): the COMPLETE `buildFlavorTokens` set for the chosen
|
|
@@ -147,8 +147,10 @@ export interface DesignRevertSnapshot extends FlavorRevertSnapshot {
|
|
|
147
147
|
* What an Apply of `plan` stores as its revert snapshot — the ONE rule the builder's Apply and the
|
|
148
148
|
* server-side site build (W2) share: the whole pre-apply token set, what the apply writes into each row,
|
|
149
149
|
* and where a revert goes back to — never a null flavor (a studio with no design row wore its design's
|
|
150
|
-
* defaults, so that is what it returns to). An apply that
|
|
151
|
-
* clobbering a useful one with "nothing changed" would be a lie.
|
|
150
|
+
* defaults, so that is what it returns to). An apply that changes NOTHING keeps the previous snapshot:
|
|
151
|
+
* clobbering a useful one with "nothing changed" would be a lie. A choice that lives in the config only
|
|
152
|
+
* (Scrolling · How animated · Between pages) IS a change — pass `next` so a motion-only Apply gets its own
|
|
153
|
+
* snapshot and Revert goes back to the motion worn just before it (vectoriox/iox-ui#28).
|
|
152
154
|
*/
|
|
153
155
|
export function designApplySnapshot(
|
|
154
156
|
previous: StoredDesign,
|
|
@@ -156,8 +158,10 @@ export function designApplySnapshot(
|
|
|
156
158
|
existing: ReadonlyArray<StoredDesignTokenRow>,
|
|
157
159
|
plan: FlavorSeedPlan,
|
|
158
160
|
appliedAt: number,
|
|
161
|
+
next?: DesignChoices,
|
|
159
162
|
): DesignRevertSnapshot | null {
|
|
160
|
-
|
|
163
|
+
const wroteTokens = plan.create.length > 0 || plan.update.length > 0;
|
|
164
|
+
if (!wroteTokens && (!next || sameDesignChoices(previous.choices, next))) return previous.revert;
|
|
161
165
|
const origin: Pick<DesignRevertSnapshot, 'from' | 'fromConfig'> = previous.storedFlavorId
|
|
162
166
|
? {
|
|
163
167
|
from: { flavorId: previous.storedFlavorId, flavorVersion: previous.flavorVersion, brandSeed: previous.storedSeed },
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { AnimateId, SPEC_AXES } from '@vectoriox/iox-ui/design-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* How animated (AI plan rev 3.1 §3p) — the ONE reading of the contract's `axes.animations` (what each level
|
|
5
|
+
* moves, how) and `axes.animationFamilies` (which parts a section family lets move). The pieces every stamper
|
|
6
|
+
* shares: the Design step's business specimen (H4-4) today, `planEntrances` in the composer (M1-6b) next. Pure.
|
|
7
|
+
*
|
|
8
|
+
* A level with `content` moves a section's content together, once; otherwise it names parts (heading, items,
|
|
9
|
+
* media…) and each part enters on its own, items one after another. Navigation, footers and globals are a family
|
|
10
|
+
* with no parts, so nothing there ever moves.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export enum EntrancePart {
|
|
14
|
+
Content = 'content',
|
|
15
|
+
Heading = 'heading',
|
|
16
|
+
Items = 'items',
|
|
17
|
+
Media = 'media',
|
|
18
|
+
RowMedia = 'rowMedia',
|
|
19
|
+
HeroText = 'heroText',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** What an entrance stamp is marked with — a later restamp touches only what the look made (H4-5). */
|
|
23
|
+
export const ENTRANCE_ORIGIN_LOOK = 'look';
|
|
24
|
+
/** The trigger every entrance plays on. */
|
|
25
|
+
export const ENTRANCE_TRIGGER = 'viewportEnter';
|
|
26
|
+
/** The easing every entrance plays with (only `opacity` and `transform` move). */
|
|
27
|
+
export const ENTRANCE_EASING = 'ease-out';
|
|
28
|
+
|
|
29
|
+
/** One entrance as the contract states it. */
|
|
30
|
+
export interface EntranceSpec {
|
|
31
|
+
readonly type: string;
|
|
32
|
+
readonly duration: number;
|
|
33
|
+
readonly distance?: number;
|
|
34
|
+
/** Items: ms added per item, up to `cap` items. */
|
|
35
|
+
readonly stagger?: number;
|
|
36
|
+
readonly cap?: number;
|
|
37
|
+
/** The type a right-to-left page plays instead (a side-by-side row slides in from ITS side). */
|
|
38
|
+
readonly mirror?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** What one section family moves at one level. `content` set = the whole content together, and no parts. */
|
|
42
|
+
export interface SectionEntrances {
|
|
43
|
+
readonly content: EntranceSpec | null;
|
|
44
|
+
readonly parts: Readonly<Partial<Record<EntrancePart, EntranceSpec>>>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The interaction action a stamp carries — the shape both interaction runtimes play. */
|
|
48
|
+
export interface EntranceAction {
|
|
49
|
+
type: string;
|
|
50
|
+
target: 'self';
|
|
51
|
+
duration: number;
|
|
52
|
+
delay: number;
|
|
53
|
+
easing: typeof ENTRANCE_EASING;
|
|
54
|
+
once: true;
|
|
55
|
+
params?: { distance: number };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface EntranceInteraction {
|
|
59
|
+
id: string;
|
|
60
|
+
trigger: typeof ENTRANCE_TRIGGER;
|
|
61
|
+
actions: [EntranceAction];
|
|
62
|
+
origin: typeof ENTRANCE_ORIGIN_LOOK;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
type Level = Readonly<{ parts: Readonly<Partial<Record<EntrancePart, EntranceSpec>>> }>;
|
|
66
|
+
const LEVELS = SPEC_AXES.animations as unknown as Readonly<Record<string, Level>>;
|
|
67
|
+
const FAMILIES = SPEC_AXES.animationFamilies as unknown as Readonly<Record<string, ReadonlyArray<EntrancePart>>>;
|
|
68
|
+
|
|
69
|
+
const NOTHING: SectionEntrances = { content: null, parts: {} };
|
|
70
|
+
|
|
71
|
+
/** What a section of `family` moves at `level` — nothing for an unknown level or family. */
|
|
72
|
+
export function sectionEntrances(level: AnimateId | string | null | undefined, family: string): SectionEntrances {
|
|
73
|
+
const lv = level ? LEVELS[level] : undefined;
|
|
74
|
+
const allow = FAMILIES[family] ?? [];
|
|
75
|
+
if (!lv || !allow.length) return NOTHING;
|
|
76
|
+
if (lv.parts.content && allow.includes(EntrancePart.Content)) return { content: lv.parts.content, parts: {} };
|
|
77
|
+
const parts: Partial<Record<EntrancePart, EntranceSpec>> = {};
|
|
78
|
+
for (const part of allow) {
|
|
79
|
+
if (part === EntrancePart.Content) continue;
|
|
80
|
+
// A level without its own side-by-side entrance gives a row's picture the plain picture one.
|
|
81
|
+
const spec = lv.parts[part] ?? (part === EntrancePart.RowMedia ? lv.parts.media : undefined);
|
|
82
|
+
if (spec) parts[part] = spec;
|
|
83
|
+
}
|
|
84
|
+
return { content: null, parts };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The literal delay of the `index`-th item: `stagger` ms each, the last `cap` waiting no longer than the one before. */
|
|
88
|
+
export function staggerDelay(spec: EntranceSpec, index = 0): number {
|
|
89
|
+
if (!spec.stagger) return 0;
|
|
90
|
+
const cap = Math.max(1, spec.cap ?? 1);
|
|
91
|
+
return Math.min(Math.max(0, index), cap - 1) * spec.stagger;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The action a node plays: the spec's type (mirrored on a right-to-left page), its distance and its delay. */
|
|
95
|
+
export function entranceAction(spec: EntranceSpec, opts: { index?: number; rtl?: boolean } = {}): EntranceAction {
|
|
96
|
+
return {
|
|
97
|
+
type: opts.rtl && spec.mirror ? spec.mirror : spec.type,
|
|
98
|
+
target: 'self',
|
|
99
|
+
duration: spec.duration,
|
|
100
|
+
delay: staggerDelay(spec, opts.index),
|
|
101
|
+
easing: ENTRANCE_EASING,
|
|
102
|
+
once: true,
|
|
103
|
+
...(spec.distance ? { params: { distance: spec.distance } } : {}),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** The stamp on a node: `viewportEnter`, once, on itself (a duplicated node needs no remap), from the look. */
|
|
108
|
+
export function entranceInteraction(nodeId: string, action: EntranceAction): EntranceInteraction {
|
|
109
|
+
return { id: `${nodeId}-enter`, trigger: ENTRANCE_TRIGGER, actions: [action], origin: ENTRANCE_ORIGIN_LOOK };
|
|
110
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { BindScope, patternById, patternNodes, patternScopes } from './patterns';
|
|
2
|
+
import { CmsDataSourcePlan, DataSourcePlan, PagePlan, PlanDataSourceMode, PlanDataSourceType, SitePlan } from './site-plan.model';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* What each page of a plan shows and where its data comes from (AI plan rev 3 §3d, `describePlanData`) — the
|
|
6
|
+
* Plan step's data lines, in the vocabulary the builder already uses. Pure and STRUCTURED: the screen words
|
|
7
|
+
* each kind in its own language ("Lists every Project", "One Project, found by its address /work/:slug").
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export enum PageDataKind {
|
|
11
|
+
/** A section repeats every item of a content type. */
|
|
12
|
+
List = 'list',
|
|
13
|
+
/** A section repeats the newest `count` items. */
|
|
14
|
+
Latest = 'latest',
|
|
15
|
+
/** A template page: one item, found by its address — one page per item. */
|
|
16
|
+
One = 'one',
|
|
17
|
+
/** The next (or previous) item beside the page's own. */
|
|
18
|
+
Siblings = 'siblings',
|
|
19
|
+
/** A form: what it sends, to the studio's inbox (+ an email when Email is connected). */
|
|
20
|
+
Form = 'form',
|
|
21
|
+
/** Words and pictures, no data. */
|
|
22
|
+
Static = 'static',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface PageDataLine {
|
|
26
|
+
kind: PageDataKind;
|
|
27
|
+
/** The content type's label ("Projects"), for every kind that reads one. */
|
|
28
|
+
contentType?: string;
|
|
29
|
+
/** Latest: how many. */
|
|
30
|
+
count?: number;
|
|
31
|
+
/** One: the page's own address, with its parameter. */
|
|
32
|
+
route?: string;
|
|
33
|
+
/** Form: the form preset (`contact`). */
|
|
34
|
+
form?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface PageData {
|
|
38
|
+
pageKey: string;
|
|
39
|
+
lines: PageDataLine[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const sameLine = (a: PageDataLine, b: PageDataLine) => JSON.stringify(a) === JSON.stringify(b);
|
|
43
|
+
|
|
44
|
+
function sourceLine(source: DataSourcePlan | undefined, label: (name: string) => string): PageDataLine | null {
|
|
45
|
+
if (!source || source.type !== PlanDataSourceType.Cms) return null;
|
|
46
|
+
const cms = source as CmsDataSourcePlan;
|
|
47
|
+
if (cms.mode !== PlanDataSourceMode.Query) return null;
|
|
48
|
+
return cms.limit
|
|
49
|
+
? { kind: PageDataKind.Latest, contentType: label(cms.contentType), count: cms.limit }
|
|
50
|
+
: { kind: PageDataKind.List, contentType: label(cms.contentType) };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function pageLines(page: PagePlan, plan: SitePlan, label: (name: string) => string): PageDataLine[] {
|
|
54
|
+
const lines: PageDataLine[] = [];
|
|
55
|
+
const add = (line: PageDataLine | null) => { if (line && !lines.some(l => sameLine(l, line))) lines.push(line); };
|
|
56
|
+
if (page.template) add({ kind: PageDataKind.One, contentType: label(page.template), route: page.route });
|
|
57
|
+
for (const section of page.sections) {
|
|
58
|
+
const pattern = patternById(section.pattern);
|
|
59
|
+
if (!pattern) continue;
|
|
60
|
+
if (section.binding) add(sourceLine(plan.dataSources.find(d => d.alias === section.binding), label));
|
|
61
|
+
if (page.template && patternScopes(pattern).has(BindScope.Next)) add({ kind: PageDataKind.Siblings, contentType: label(page.template) });
|
|
62
|
+
for (const node of patternNodes(pattern)) if (node.preset) add({ kind: PageDataKind.Form, form: node.preset });
|
|
63
|
+
}
|
|
64
|
+
return lines.length ? lines : [{ kind: PageDataKind.Static }];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** One entry per page of the plan, in plan order. */
|
|
68
|
+
export function describePlanData(plan: SitePlan): PageData[] {
|
|
69
|
+
const label = (name: string) => plan.contentTypes.find(t => t.name === name)?.label ?? name;
|
|
70
|
+
return plan.pages.map(page => ({ pageKey: page.key, lines: pageLines(page, plan, label) }));
|
|
71
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { BUSINESS_TYPES, BusinessType } from './business-types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What Start reads from the owner's words BEFORE any model runs (AI plan rev 3 §3d step 0, N-D3): the kind of
|
|
5
|
+
* business by the contract's own keywords (`BusinessType.match` — "the no-model fallback"), and the addresses
|
|
6
|
+
* typed into the box (each becomes a "style only" reference chip, R1). Pure; the template path uses it today and
|
|
7
|
+
* the reading stage (M1-1) checks the model against the same match.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** A single Hebrew letter that prefixes a word (the, in, to, and, from, that, as) — "בסדנאות" holds "סדנ". */
|
|
11
|
+
const HEBREW_PREFIX = '[הבלומשכ]?';
|
|
12
|
+
|
|
13
|
+
const escapeRe = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
14
|
+
|
|
15
|
+
const KEYWORD_RES = new Map<string, RegExp>();
|
|
16
|
+
|
|
17
|
+
/** A keyword matches at the START of a word (so a stem — "סדנ", "consult" — matches its forms). */
|
|
18
|
+
function keywordRe(keyword: string): RegExp {
|
|
19
|
+
let re = KEYWORD_RES.get(keyword);
|
|
20
|
+
if (!re) {
|
|
21
|
+
re = new RegExp(`(^|[^\\p{L}\\p{N}])${HEBREW_PREFIX}${escapeRe(keyword.toLocaleLowerCase())}`, 'u');
|
|
22
|
+
KEYWORD_RES.set(keyword, re);
|
|
23
|
+
}
|
|
24
|
+
return re;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface BusinessTypeMatch {
|
|
28
|
+
id: string;
|
|
29
|
+
/** The keywords found, in the type's order. */
|
|
30
|
+
keywords: string[];
|
|
31
|
+
/** The letters matched — longer, more specific keywords weigh more ("barber" over "bar"). */
|
|
32
|
+
score: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The kind of business the words name, best first; empty when none of the keywords appears. Ties keep the board's order. */
|
|
36
|
+
export function matchBusinessTypes(text: string, types: ReadonlyArray<BusinessType> = BUSINESS_TYPES): BusinessTypeMatch[] {
|
|
37
|
+
const words = (text ?? '').toLocaleLowerCase();
|
|
38
|
+
if (!words.trim()) return [];
|
|
39
|
+
return types
|
|
40
|
+
.map((type, order) => {
|
|
41
|
+
const keywords = type.match.filter(k => keywordRe(k).test(words));
|
|
42
|
+
return { id: type.id, keywords, score: keywords.reduce((n, k) => n + k.length, 0), order };
|
|
43
|
+
})
|
|
44
|
+
.filter(m => m.score > 0)
|
|
45
|
+
.sort((a, b) => b.score - a.score || a.order - b.order)
|
|
46
|
+
.map(({ order, ...m }) => m);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The single best kind of business the words name, or null. */
|
|
50
|
+
export function matchBusinessType(text: string, types: ReadonlyArray<BusinessType> = BUSINESS_TYPES): string | null {
|
|
51
|
+
return matchBusinessTypes(text, types)[0]?.id ?? null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const ADDRESS_RE = /(?:https?:\/\/)?(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,}(?:\/[^\s<>"']*)?/gi;
|
|
55
|
+
const TRAILING = /[.,;:!?)\]}'"״׳]+$/;
|
|
56
|
+
|
|
57
|
+
/** An address as a chip shows it: no scheme, no `www.`, no trailing punctuation or slash, lower-case host. */
|
|
58
|
+
export function normalizeAddress(raw: string): string {
|
|
59
|
+
const bare = raw.replace(TRAILING, '').replace(/^https?:\/\//i, '').replace(/^www\./i, '').replace(/\/+$/, '');
|
|
60
|
+
const slash = bare.indexOf('/');
|
|
61
|
+
return slash < 0 ? bare.toLowerCase() : bare.slice(0, slash).toLowerCase() + bare.slice(slash);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Every website address typed or pasted in the words (an http(s) address or a bare domain), once each, in
|
|
65
|
+
* order. An email address is not a website. */
|
|
66
|
+
export function addressesIn(text: string): string[] {
|
|
67
|
+
const found: string[] = [];
|
|
68
|
+
for (const m of (text ?? '').matchAll(ADDRESS_RE)) {
|
|
69
|
+
const before = m.index ? text[m.index - 1] : '';
|
|
70
|
+
if (before === '@' || /[\p{L}\p{N}_-]/u.test(before)) continue;
|
|
71
|
+
const address = normalizeAddress(m[0]);
|
|
72
|
+
if (address && !found.includes(address)) found.push(address);
|
|
73
|
+
}
|
|
74
|
+
return found;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The words without one address (removing its chip): every spelling of it, and the space it leaves. */
|
|
78
|
+
export function withoutAddress(text: string, address: string): string {
|
|
79
|
+
const out = (text ?? '').replace(ADDRESS_RE, (m, offset: number, whole: string) => {
|
|
80
|
+
const before = offset ? whole[offset - 1] : '';
|
|
81
|
+
if (before === '@' || /[\p{L}\p{N}_-]/u.test(before)) return m;
|
|
82
|
+
return normalizeAddress(m) === address ? '' : m;
|
|
83
|
+
});
|
|
84
|
+
return out.replace(/[ \t]{2,}/g, ' ').replace(/ +([.,;:!?])/g, '$1').trim();
|
|
85
|
+
}
|