@vectoriox/iox-ui 5.47.0 → 5.49.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 +89 -7
- 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 +40 -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/lint.ts +16 -4
- package/src/harness-core/motion.ts +18 -3
- package/src/harness-core/public-api.ts +1 -0
|
@@ -3778,11 +3778,13 @@ function motionFor(input, presetId, read = null, types = BUSINESS_TYPES, presets
|
|
|
3778
3778
|
const wanted = ref ?? base.animate;
|
|
3779
3779
|
let out = { scroll: base.scroll, animate: calmerChoice('animate', wanted, preset.moves.animate), between: base.between };
|
|
3780
3780
|
const why = [type ? `${type.label}: ${triple(base)}` : `${preset.name}: the style's own`];
|
|
3781
|
-
|
|
3781
|
+
const basis = { type: type?.id ?? null, calmerStyle: out.animate !== wanted, references: !!(ref && type), littleMovement: false };
|
|
3782
|
+
if (basis.calmerStyle)
|
|
3782
3783
|
why.push(`${preset.name} moves less (${label('animate', preset.moves.animate)})`);
|
|
3783
|
-
if (
|
|
3784
|
+
if (basis.references)
|
|
3784
3785
|
why.push(`your references: ${label('animate', ref)}`);
|
|
3785
3786
|
if ((input.avoid ?? []).includes(LITTLE_MOVEMENT_AVOID)) {
|
|
3787
|
+
basis.littleMovement = true;
|
|
3786
3788
|
out = {
|
|
3787
3789
|
scroll: calmerChoice('scroll', out.scroll, LITTLE_MOVEMENT_CAP.scroll),
|
|
3788
3790
|
animate: calmerChoice('animate', out.animate, LITTLE_MOVEMENT_CAP.animate),
|
|
@@ -3790,7 +3792,7 @@ function motionFor(input, presetId, read = null, types = BUSINESS_TYPES, presets
|
|
|
3790
3792
|
};
|
|
3791
3793
|
why.push('you asked for little movement');
|
|
3792
3794
|
}
|
|
3793
|
-
return { ...out, why };
|
|
3795
|
+
return { ...out, why, basis };
|
|
3794
3796
|
}
|
|
3795
3797
|
|
|
3796
3798
|
/** Who asked for a need: the owner named it, or the kind of business needs it ("we added"). */
|
|
@@ -3890,13 +3892,23 @@ function lintDesign(site, typeId, types = BUSINESS_TYPES) {
|
|
|
3890
3892
|
}
|
|
3891
3893
|
}
|
|
3892
3894
|
}
|
|
3893
|
-
|
|
3894
|
-
if (
|
|
3895
|
-
const usual = SPEC_AXES.animations[
|
|
3895
|
+
const advice = motionAdvice(type.id, site.design.animate, types);
|
|
3896
|
+
if (advice) {
|
|
3897
|
+
const usual = SPEC_AXES.animations[advice.usual].label;
|
|
3896
3898
|
out.push({ kind: LintKind.Look, say: `${type.label} sites usually move less (${usual}); this much movement as people scroll can feel restless.` });
|
|
3897
3899
|
}
|
|
3898
3900
|
return out;
|
|
3899
3901
|
}
|
|
3902
|
+
/**
|
|
3903
|
+
* Rev 3.1: two levels above what this kind of business usually wears (Subtle → Lively) is advice, never a block.
|
|
3904
|
+
* Structured, so Design ▸ Motion says it in the studio's language: `usual` = the level the type wears.
|
|
3905
|
+
*/
|
|
3906
|
+
function motionAdvice(typeId, animate, types = BUSINESS_TYPES) {
|
|
3907
|
+
const type = typeId ? types.find(t => t.id === typeId) : undefined;
|
|
3908
|
+
if (!type || !animate || !ANIMATE_ORDER.includes(animate))
|
|
3909
|
+
return null;
|
|
3910
|
+
return ANIMATE_ORDER.indexOf(animate) >= ANIMATE_ORDER.indexOf(type.moves.animate) + ANIMATE_ADVICE_STEPS ? { usual: type.moves.animate } : null;
|
|
3911
|
+
}
|
|
3900
3912
|
|
|
3901
3913
|
/**
|
|
3902
3914
|
* What the reading of a prompt returns (AI plan rev 3 §3d): one of the 30 kinds of business (and the two
|
|
@@ -4091,6 +4103,76 @@ function analysisPromptContext(analysis, opts = {}) {
|
|
|
4091
4103
|
};
|
|
4092
4104
|
}
|
|
4093
4105
|
|
|
4106
|
+
/**
|
|
4107
|
+
* What Start reads from the owner's words BEFORE any model runs (AI plan rev 3 §3d step 0, N-D3): the kind of
|
|
4108
|
+
* business by the contract's own keywords (`BusinessType.match` — "the no-model fallback"), and the addresses
|
|
4109
|
+
* typed into the box (each becomes a "style only" reference chip, R1). Pure; the template path uses it today and
|
|
4110
|
+
* the reading stage (M1-1) checks the model against the same match.
|
|
4111
|
+
*/
|
|
4112
|
+
/** A single Hebrew letter that prefixes a word (the, in, to, and, from, that, as) — "בסדנאות" holds "סדנ". */
|
|
4113
|
+
const HEBREW_PREFIX = '[הבלומשכ]?';
|
|
4114
|
+
const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
4115
|
+
const KEYWORD_RES = new Map();
|
|
4116
|
+
/** A keyword matches at the START of a word (so a stem — "סדנ", "consult" — matches its forms). */
|
|
4117
|
+
function keywordRe(keyword) {
|
|
4118
|
+
let re = KEYWORD_RES.get(keyword);
|
|
4119
|
+
if (!re) {
|
|
4120
|
+
re = new RegExp(`(^|[^\\p{L}\\p{N}])${HEBREW_PREFIX}${escapeRe(keyword.toLocaleLowerCase())}`, 'u');
|
|
4121
|
+
KEYWORD_RES.set(keyword, re);
|
|
4122
|
+
}
|
|
4123
|
+
return re;
|
|
4124
|
+
}
|
|
4125
|
+
/** The kind of business the words name, best first; empty when none of the keywords appears. Ties keep the board's order. */
|
|
4126
|
+
function matchBusinessTypes(text, types = BUSINESS_TYPES) {
|
|
4127
|
+
const words = (text ?? '').toLocaleLowerCase();
|
|
4128
|
+
if (!words.trim())
|
|
4129
|
+
return [];
|
|
4130
|
+
return types
|
|
4131
|
+
.map((type, order) => {
|
|
4132
|
+
const keywords = type.match.filter(k => keywordRe(k).test(words));
|
|
4133
|
+
return { id: type.id, keywords, score: keywords.reduce((n, k) => n + k.length, 0), order };
|
|
4134
|
+
})
|
|
4135
|
+
.filter(m => m.score > 0)
|
|
4136
|
+
.sort((a, b) => b.score - a.score || a.order - b.order)
|
|
4137
|
+
.map(({ order, ...m }) => m);
|
|
4138
|
+
}
|
|
4139
|
+
/** The single best kind of business the words name, or null. */
|
|
4140
|
+
function matchBusinessType(text, types = BUSINESS_TYPES) {
|
|
4141
|
+
return matchBusinessTypes(text, types)[0]?.id ?? null;
|
|
4142
|
+
}
|
|
4143
|
+
const ADDRESS_RE = /(?:https?:\/\/)?(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,}(?:\/[^\s<>"']*)?/gi;
|
|
4144
|
+
const TRAILING = /[.,;:!?)\]}'"״׳]+$/;
|
|
4145
|
+
/** An address as a chip shows it: no scheme, no `www.`, no trailing punctuation or slash, lower-case host. */
|
|
4146
|
+
function normalizeAddress(raw) {
|
|
4147
|
+
const bare = raw.replace(TRAILING, '').replace(/^https?:\/\//i, '').replace(/^www\./i, '').replace(/\/+$/, '');
|
|
4148
|
+
const slash = bare.indexOf('/');
|
|
4149
|
+
return slash < 0 ? bare.toLowerCase() : bare.slice(0, slash).toLowerCase() + bare.slice(slash);
|
|
4150
|
+
}
|
|
4151
|
+
/** Every website address typed or pasted in the words (an http(s) address or a bare domain), once each, in
|
|
4152
|
+
* order. An email address is not a website. */
|
|
4153
|
+
function addressesIn(text) {
|
|
4154
|
+
const found = [];
|
|
4155
|
+
for (const m of (text ?? '').matchAll(ADDRESS_RE)) {
|
|
4156
|
+
const before = m.index ? text[m.index - 1] : '';
|
|
4157
|
+
if (before === '@' || /[\p{L}\p{N}_-]/u.test(before))
|
|
4158
|
+
continue;
|
|
4159
|
+
const address = normalizeAddress(m[0]);
|
|
4160
|
+
if (address && !found.includes(address))
|
|
4161
|
+
found.push(address);
|
|
4162
|
+
}
|
|
4163
|
+
return found;
|
|
4164
|
+
}
|
|
4165
|
+
/** The words without one address (removing its chip): every spelling of it, and the space it leaves. */
|
|
4166
|
+
function withoutAddress(text, address) {
|
|
4167
|
+
const out = (text ?? '').replace(ADDRESS_RE, (m, offset, whole) => {
|
|
4168
|
+
const before = offset ? whole[offset - 1] : '';
|
|
4169
|
+
if (before === '@' || /[\p{L}\p{N}_-]/u.test(before))
|
|
4170
|
+
return m;
|
|
4171
|
+
return normalizeAddress(m) === address ? '' : m;
|
|
4172
|
+
});
|
|
4173
|
+
return out.replace(/[ \t]{2,}/g, ' ').replace(/ +([.,;:!?])/g, '$1').trim();
|
|
4174
|
+
}
|
|
4175
|
+
|
|
4094
4176
|
/**
|
|
4095
4177
|
* @vectoriox/iox-ui/harness-core — the design HARNESS (AI plan rev 3 §3f, decision N18), FRAMEWORK-FREE: the 30
|
|
4096
4178
|
* kinds of business and their rules (generated 1:1 from iox-ai-guidance `flavor-spec.v1.json` `harness`), the
|
|
@@ -4105,5 +4187,5 @@ function analysisPromptContext(analysis, opts = {}) {
|
|
|
4105
4187
|
* Generated bundle index. Do not edit.
|
|
4106
4188
|
*/
|
|
4107
4189
|
|
|
4108
|
-
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, analysisPromptContext, businessTypeById, calmerChoice, inferNeeds, isLookRule, lintDesign, lookMatches, mapStyleRead, motionFor, oklchHue, presetHasSerifHeadings, presetLook, rankStylePresets, rankWithReferences, userAvoidRule, validateSiteAnalysis };
|
|
4190
|
+
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, motionAdvice, motionFor, normalizeAddress, oklchHue, presetHasSerifHeadings, presetLook, rankStylePresets, rankWithReferences, userAvoidRule, validateSiteAnalysis, withoutAddress };
|
|
4109
4191
|
//# sourceMappingURL=vectoriox-iox-ui-harness-core.mjs.map
|