@vectoriox/iox-ui 5.51.0 → 5.53.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 +27 -0
- package/fesm2022/vectoriox-iox-ui-design-core.mjs +24 -0
- package/fesm2022/vectoriox-iox-ui-design-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-generation-core.mjs +142 -1
- package/fesm2022/vectoriox-iox-ui-generation-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-harness-core.mjs +496 -311
- package/fesm2022/vectoriox-iox-ui-harness-core.mjs.map +1 -1
- package/generation-core/index.d.ts +146 -2
- package/harness-core/index.d.ts +147 -2
- package/package.json +1 -1
- package/src/design-core/flavor-spec.generated.ts +24 -0
- package/src/design-core/style-presets.ts +3 -0
- package/src/generation-core/ai-run.ts +205 -0
- package/src/generation-core/public-api.ts +1 -0
- package/src/harness-core/business-types.ts +2 -0
- package/src/harness-core/harness-spec.generated.ts +240 -120
- package/src/harness-core/rank.ts +17 -1
- package/src/harness-core/site-analysis.ts +70 -0
package/src/harness-core/rank.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { AnimateId } from '@vectoriox/iox-ui/design-core';
|
|
|
5
5
|
|
|
6
6
|
import { AvoidRule, BUSINESS_TYPES, BusinessType, isLookRule, LookRule, userAvoidRule } from './business-types';
|
|
7
7
|
import { lookMatches, presetLook } from './look';
|
|
8
|
+
import { AnalysisLocale } from './site-analysis';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Which styles suit a business, best first (AI plan rev 3 §3f, H3) — Design ▸ Look's order. Pure and
|
|
@@ -45,6 +46,8 @@ export interface RankedPreset {
|
|
|
45
46
|
reasonKind: RankReason;
|
|
46
47
|
/** In English, from the contract (the UI may localise `reasonKind` + `sharedMoods` instead). */
|
|
47
48
|
reason: string;
|
|
49
|
+
/** A good fit's reason in Hebrew (the contract has Hebrew for good fits; `presetReason` picks). */
|
|
50
|
+
reasonHe?: string;
|
|
48
51
|
sharedMoods: MoodId[];
|
|
49
52
|
/** Catalogue position — ties keep it. */
|
|
50
53
|
order: number;
|
|
@@ -113,10 +116,23 @@ export function rankStylePresets(
|
|
|
113
116
|
: goodAt >= 0 && type ? [RankReason.GoodFit, type.good[goodAt].reason]
|
|
114
117
|
: sharedMoods.length ? [RankReason.SharedMoods, moodsLine(sharedMoods)]
|
|
115
118
|
: [RankReason.None, ''];
|
|
116
|
-
|
|
119
|
+
const reasonHe = reasonKind === RankReason.GoodFit && type ? type.good[goodAt].reasonHe : undefined;
|
|
120
|
+
return { id: preset.id, fit, score, reasonKind, reason, ...(reasonHe ? { reasonHe } : {}), sharedMoods, order };
|
|
117
121
|
}).sort((a, b) => b.score - a.score || a.order - b.order);
|
|
118
122
|
}
|
|
119
123
|
|
|
124
|
+
/**
|
|
125
|
+
* The line a style's card shows (its title): why it sits where it does, else what the style is — in the studio's
|
|
126
|
+
* language where the contract has it. Decided 2026-10-25: good fits and descriptions have Hebrew (the name stays a
|
|
127
|
+
* proper name); a type's or the owner's advice and the shared-moods line are English in the contract yet.
|
|
128
|
+
*/
|
|
129
|
+
export function presetReason(ranked: RankedPreset, preset: StylePreset | undefined, locale: string | null | undefined): string {
|
|
130
|
+
const hebrew = locale === AnalysisLocale.He;
|
|
131
|
+
if (hebrew && ranked.reasonKind === RankReason.GoodFit && ranked.reasonHe) return ranked.reasonHe;
|
|
132
|
+
if (ranked.reason) return ranked.reason;
|
|
133
|
+
return (hebrew ? preset?.blurbHe : preset?.blurb) ?? '';
|
|
134
|
+
}
|
|
135
|
+
|
|
120
136
|
// ── References: a reading of pictures or a site, as style (plan §3k, R4) ─────────────────────────────────
|
|
121
137
|
export enum StyleReadSourceKind {
|
|
122
138
|
Image = 'image',
|
|
@@ -178,3 +178,73 @@ export function validateSiteAnalysis(value: unknown, ctx: AnalysisContext): Anal
|
|
|
178
178
|
|
|
179
179
|
return { valid: c.issues.length === 0, issues: c.issues };
|
|
180
180
|
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The structured-output schema a model is given to write a `SiteAnalysis` (AI plan §3c/§3d, M0-5): built from the
|
|
184
|
+
* SAME shape table `validateSiteAnalysis` reads (keys, which are required, each key's explanation as its
|
|
185
|
+
* description) and the same closed lists (30 business types, needs, facts, moods, avoids) — so the shape the model
|
|
186
|
+
* is told about is the shape the gate refuses. Written to the strictest common subset of JSON Schema the providers'
|
|
187
|
+
* native strict modes accept (Anthropic `output_config.format`, OpenRouter `json_schema` + `strict`): every object
|
|
188
|
+
* closes with `additionalProperties: false`; no length, count or number bounds (the gate checks those). The
|
|
189
|
+
* schema is a hint the provider enforces; `validateSiteAnalysis` stays the gate.
|
|
190
|
+
*/
|
|
191
|
+
|
|
192
|
+
export type JsonSchema = { [key: string]: unknown };
|
|
193
|
+
|
|
194
|
+
/** The name a provider shows the format under (`json_schema.name`). */
|
|
195
|
+
export const SITE_ANALYSIS_SCHEMA_NAME = 'site_analysis';
|
|
196
|
+
|
|
197
|
+
const OPTIONAL_MARK = '?';
|
|
198
|
+
|
|
199
|
+
const text = (): JsonSchema => ({ type: 'string' });
|
|
200
|
+
const oneOf = (values: ReadonlyArray<string>): JsonSchema => ({ type: 'string', enum: [...values] });
|
|
201
|
+
const listOf = (items: JsonSchema): JsonSchema => ({ type: 'array', items });
|
|
202
|
+
|
|
203
|
+
/** The shape table's own words for a key — the description the model reads. */
|
|
204
|
+
function describe(shape: string, key: string): string {
|
|
205
|
+
const entry = SHAPE[shape];
|
|
206
|
+
return String(entry[key] ?? entry[`${key}${OPTIONAL_MARK}`] ?? '');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** An object of `shape`: exactly its keys, its required ones required, each described by the table. */
|
|
210
|
+
function shaped(shape: string, properties: Record<string, JsonSchema>): JsonSchema {
|
|
211
|
+
return {
|
|
212
|
+
type: 'object',
|
|
213
|
+
properties: Object.fromEntries(Object.entries(properties).map(([key, schema]) => [key, { ...schema, description: describe(shape, key) }])),
|
|
214
|
+
required: requiredShapeKeys(SHAPE[shape]),
|
|
215
|
+
additionalProperties: false,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** `{ [fact]: factRead }`: the closed fact keys, each optional — a fact the prompt does not state is left out. */
|
|
220
|
+
function factsSchema(factRead: JsonSchema): JsonSchema {
|
|
221
|
+
return {
|
|
222
|
+
type: 'object',
|
|
223
|
+
properties: Object.fromEntries(FACT_KEYS.map(key => [key, factRead])),
|
|
224
|
+
required: [],
|
|
225
|
+
additionalProperties: false,
|
|
226
|
+
description: describe('analysis', 'facts'),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function siteAnalysisJsonSchema(): JsonSchema {
|
|
231
|
+
const factRead = shaped('factRead', { value: text(), quote: text() });
|
|
232
|
+
return shaped('analysis', {
|
|
233
|
+
businessType: oneOf(BUSINESS_TYPE_IDS),
|
|
234
|
+
alternatives: listOf(oneOf(BUSINESS_TYPE_IDS)),
|
|
235
|
+
business: shaped('business', { name: text(), audience: text() }),
|
|
236
|
+
needs: listOf(shaped('need', { id: oneOf(NEED_IDS), from: oneOf(Object.values(NeedSource)), name: text() })),
|
|
237
|
+
facts: factsSchema(factRead),
|
|
238
|
+
missingFacts: listOf(oneOf(FACT_KEYS)),
|
|
239
|
+
voice: shaped('voice', {
|
|
240
|
+
tone: text(),
|
|
241
|
+
person: oneOf(Object.values(VoicePerson)),
|
|
242
|
+
formality: oneOf(Object.values(VoiceFormality)),
|
|
243
|
+
avoidWords: listOf(text()),
|
|
244
|
+
}),
|
|
245
|
+
moods: listOf(oneOf(MOOD_IDS)),
|
|
246
|
+
avoid: listOf(oneOf(USER_AVOID_IDS)),
|
|
247
|
+
hero: shaped('hero', { headline: text(), lead: text() }),
|
|
248
|
+
locale: oneOf(Object.values(AnalysisLocale)),
|
|
249
|
+
});
|
|
250
|
+
}
|