Update storyboard
This commit is contained in:
parent
1483dc5224
commit
a9e5a8ad96
7 changed files with 49 additions and 103 deletions
|
|
@ -202,7 +202,7 @@ const de: Translations = {
|
||||||
clearAllSavePrompt: 'Möchtest du deine aktuellen Filter vor dem Löschen speichern?',
|
clearAllSavePrompt: 'Möchtest du deine aktuellen Filter vor dem Löschen speichern?',
|
||||||
saveAndClear: 'Speichern & löschen',
|
saveAndClear: 'Speichern & löschen',
|
||||||
clearWithoutSaving: 'Ohne Speichern löschen',
|
clearWithoutSaving: 'Ohne Speichern löschen',
|
||||||
withoutThisFilter: '+{{value}} ohne diesen Filter',
|
filtersOut: 'filtert {{value}} heraus',
|
||||||
schoolType: 'Schultyp',
|
schoolType: 'Schultyp',
|
||||||
schoolRating: 'Schulbewertung',
|
schoolRating: 'Schulbewertung',
|
||||||
schoolDistance: 'Schulentfernung',
|
schoolDistance: 'Schulentfernung',
|
||||||
|
|
@ -898,7 +898,7 @@ const de: Translations = {
|
||||||
Properties: 'Immobilien',
|
Properties: 'Immobilien',
|
||||||
Transport: 'Verkehr',
|
Transport: 'Verkehr',
|
||||||
Education: 'Bildung',
|
Education: 'Bildung',
|
||||||
'Area characteristics': 'Gebietsmerkmale',
|
'Area development': 'Gebietsentwicklung',
|
||||||
Crime: 'Kriminalität',
|
Crime: 'Kriminalität',
|
||||||
Neighbours: 'Nachbarn',
|
Neighbours: 'Nachbarn',
|
||||||
Amenities: 'Infrastruktur',
|
Amenities: 'Infrastruktur',
|
||||||
|
|
@ -939,7 +939,7 @@ const de: Translations = {
|
||||||
'Hervorragende weiterführende Schulen im Umkreis von 5 km',
|
'Hervorragende weiterführende Schulen im Umkreis von 5 km',
|
||||||
'Education, Skills and Training Score': 'Score für Bildung, Kompetenzen und Ausbildung',
|
'Education, Skills and Training Score': 'Score für Bildung, Kompetenzen und Ausbildung',
|
||||||
|
|
||||||
// ─ Feature names (Area characteristics) ─
|
// ─ Feature names (Area development) ─
|
||||||
'Income Score': 'Einkommensscore',
|
'Income Score': 'Einkommensscore',
|
||||||
'Employment Score': 'Beschäftigungsscore',
|
'Employment Score': 'Beschäftigungsscore',
|
||||||
'Health Deprivation and Disability Score': 'Score für Gesundheit und Behinderung',
|
'Health Deprivation and Disability Score': 'Score für Gesundheit und Behinderung',
|
||||||
|
|
@ -995,9 +995,9 @@ const de: Translations = {
|
||||||
Schools: 'Schulen',
|
Schools: 'Schulen',
|
||||||
'Specific crimes': 'Einzelne Delikte',
|
'Specific crimes': 'Einzelne Delikte',
|
||||||
Ethnicities: 'Ethnien',
|
Ethnicities: 'Ethnien',
|
||||||
'POI distance': 'POI-Entfernung',
|
'Amenity distance': 'Entfernung zu Infrastruktur',
|
||||||
'POIs within 2km': 'POIs innerhalb von 2 km',
|
'Amenities within 2km': 'Infrastruktur im Umkreis von 2 km',
|
||||||
'POIs within 5km': 'POIs innerhalb von 5 km',
|
'Amenities within 5km': 'Infrastruktur im Umkreis von 5 km',
|
||||||
|
|
||||||
// ─ Enum values ─
|
// ─ Enum values ─
|
||||||
Detached: 'Freistehend',
|
Detached: 'Freistehend',
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ const en = {
|
||||||
clearAllSavePrompt: 'Would you like to save your current filters before clearing?',
|
clearAllSavePrompt: 'Would you like to save your current filters before clearing?',
|
||||||
saveAndClear: 'Save & Clear',
|
saveAndClear: 'Save & Clear',
|
||||||
clearWithoutSaving: 'Clear without saving',
|
clearWithoutSaving: 'Clear without saving',
|
||||||
withoutThisFilter: '+{{value}} without this filter',
|
filtersOut: 'filters out {{value}}',
|
||||||
schoolType: 'School type',
|
schoolType: 'School type',
|
||||||
schoolRating: 'School rating',
|
schoolRating: 'School rating',
|
||||||
schoolDistance: 'School distance',
|
schoolDistance: 'School distance',
|
||||||
|
|
@ -882,7 +882,7 @@ const en = {
|
||||||
Properties: 'Properties',
|
Properties: 'Properties',
|
||||||
Transport: 'Transport',
|
Transport: 'Transport',
|
||||||
Education: 'Education',
|
Education: 'Education',
|
||||||
'Area characteristics': 'Area characteristics',
|
'Area development': 'Area development',
|
||||||
Crime: 'Crime',
|
Crime: 'Crime',
|
||||||
Neighbours: 'Neighbours',
|
Neighbours: 'Neighbours',
|
||||||
Amenities: 'Amenities',
|
Amenities: 'Amenities',
|
||||||
|
|
@ -921,7 +921,7 @@ const en = {
|
||||||
'Outstanding secondary schools within 5km': 'Outstanding secondary schools within 5km',
|
'Outstanding secondary schools within 5km': 'Outstanding secondary schools within 5km',
|
||||||
'Education, Skills and Training Score': 'Education, Skills and Training Score',
|
'Education, Skills and Training Score': 'Education, Skills and Training Score',
|
||||||
|
|
||||||
// ─ Feature names (Area characteristics) ─
|
// ─ Feature names (Area development) ─
|
||||||
'Income Score': 'Income Score',
|
'Income Score': 'Income Score',
|
||||||
'Employment Score': 'Employment Score',
|
'Employment Score': 'Employment Score',
|
||||||
'Health Deprivation and Disability Score': 'Health Deprivation and Disability Score',
|
'Health Deprivation and Disability Score': 'Health Deprivation and Disability Score',
|
||||||
|
|
@ -975,9 +975,9 @@ const en = {
|
||||||
Schools: 'Schools',
|
Schools: 'Schools',
|
||||||
'Specific crimes': 'Specific crimes',
|
'Specific crimes': 'Specific crimes',
|
||||||
Ethnicities: 'Ethnicities',
|
Ethnicities: 'Ethnicities',
|
||||||
'POI distance': 'POI distance',
|
'Amenity distance': 'Amenity distance',
|
||||||
'POIs within 2km': 'POIs within 2km',
|
'Amenities within 2km': 'Amenities within 2km',
|
||||||
'POIs within 5km': 'POIs within 5km',
|
'Amenities within 5km': 'Amenities within 5km',
|
||||||
|
|
||||||
// ─ Enum values ─
|
// ─ Enum values ─
|
||||||
Detached: 'Detached',
|
Detached: 'Detached',
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ const fr: Translations = {
|
||||||
clearAllSavePrompt: 'Souhaitez-vous sauvegarder vos filtres actuels avant de les effacer ?',
|
clearAllSavePrompt: 'Souhaitez-vous sauvegarder vos filtres actuels avant de les effacer ?',
|
||||||
saveAndClear: 'Sauvegarder et effacer',
|
saveAndClear: 'Sauvegarder et effacer',
|
||||||
clearWithoutSaving: 'Effacer sans sauvegarder',
|
clearWithoutSaving: 'Effacer sans sauvegarder',
|
||||||
withoutThisFilter: '+{{value}} sans ce filtre',
|
filtersOut: 'exclut {{value}}',
|
||||||
schoolType: 'Type d’école',
|
schoolType: 'Type d’école',
|
||||||
schoolRating: 'Note de l’école',
|
schoolRating: 'Note de l’école',
|
||||||
schoolDistance: 'Distance de l’école',
|
schoolDistance: 'Distance de l’école',
|
||||||
|
|
@ -901,7 +901,7 @@ const fr: Translations = {
|
||||||
Properties: 'Propriétés',
|
Properties: 'Propriétés',
|
||||||
Transport: 'Transports',
|
Transport: 'Transports',
|
||||||
Education: 'Éducation',
|
Education: 'Éducation',
|
||||||
'Area characteristics': 'Caractéristiques du quartier',
|
'Area development': 'Développement du quartier',
|
||||||
Crime: 'Criminalité',
|
Crime: 'Criminalité',
|
||||||
Neighbours: 'Voisins',
|
Neighbours: 'Voisins',
|
||||||
Amenities: 'Commodités',
|
Amenities: 'Commodités',
|
||||||
|
|
@ -940,7 +940,7 @@ const fr: Translations = {
|
||||||
'Outstanding secondary schools within 5km': 'Collèges/lycées Excellent dans un rayon de 5 km',
|
'Outstanding secondary schools within 5km': 'Collèges/lycées Excellent dans un rayon de 5 km',
|
||||||
'Education, Skills and Training Score': 'Score éducation, compétences et formation',
|
'Education, Skills and Training Score': 'Score éducation, compétences et formation',
|
||||||
|
|
||||||
// ─ Feature names (Area characteristics) ─
|
// ─ Feature names (Area development) ─
|
||||||
'Income Score': 'Score de revenu',
|
'Income Score': 'Score de revenu',
|
||||||
'Employment Score': 'Score d’emploi',
|
'Employment Score': 'Score d’emploi',
|
||||||
'Health Deprivation and Disability Score': 'Score de santé et handicap',
|
'Health Deprivation and Disability Score': 'Score de santé et handicap',
|
||||||
|
|
@ -994,9 +994,9 @@ const fr: Translations = {
|
||||||
Schools: 'Écoles',
|
Schools: 'Écoles',
|
||||||
'Specific crimes': 'Crimes spécifiques',
|
'Specific crimes': 'Crimes spécifiques',
|
||||||
Ethnicities: 'Origines ethniques',
|
Ethnicities: 'Origines ethniques',
|
||||||
'POI distance': 'Distance aux POI',
|
'Amenity distance': 'Distance aux commodités',
|
||||||
'POIs within 2km': 'POI à moins de 2 km',
|
'Amenities within 2km': 'Commodités à moins de 2 km',
|
||||||
'POIs within 5km': 'POI à moins de 5 km',
|
'Amenities within 5km': 'Commodités à moins de 5 km',
|
||||||
|
|
||||||
// ─ Enum values ─
|
// ─ Enum values ─
|
||||||
Detached: 'Individuelle',
|
Detached: 'Individuelle',
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ const hi: Translations = {
|
||||||
clearAllSavePrompt: 'क्या साफ करने से पहले आप अपने मौजूदा फिल्टर सहेजना चाहेंगे?',
|
clearAllSavePrompt: 'क्या साफ करने से पहले आप अपने मौजूदा फिल्टर सहेजना चाहेंगे?',
|
||||||
saveAndClear: 'सहेजें और साफ करें',
|
saveAndClear: 'सहेजें और साफ करें',
|
||||||
clearWithoutSaving: 'बिना सहेजे साफ करें',
|
clearWithoutSaving: 'बिना सहेजे साफ करें',
|
||||||
withoutThisFilter: '+{{value}} इस फिल्टर के बिना',
|
filtersOut: '{{value}} को फिल्टर करता है',
|
||||||
schoolType: 'स्कूल प्रकार',
|
schoolType: 'स्कूल प्रकार',
|
||||||
schoolRating: 'स्कूल रेटिंग',
|
schoolRating: 'स्कूल रेटिंग',
|
||||||
schoolDistance: 'स्कूल दूरी',
|
schoolDistance: 'स्कूल दूरी',
|
||||||
|
|
@ -833,7 +833,7 @@ const hi: Translations = {
|
||||||
Properties: 'संपत्तियां',
|
Properties: 'संपत्तियां',
|
||||||
Transport: 'परिवहन',
|
Transport: 'परिवहन',
|
||||||
Education: 'शिक्षा',
|
Education: 'शिक्षा',
|
||||||
'Area characteristics': 'क्षेत्र की विशेषताएँ',
|
'Area development': 'क्षेत्र विकास',
|
||||||
Crime: 'अपराध',
|
Crime: 'अपराध',
|
||||||
Neighbours: 'पड़ोसी',
|
Neighbours: 'पड़ोसी',
|
||||||
Amenities: 'सुविधाएं',
|
Amenities: 'सुविधाएं',
|
||||||
|
|
@ -911,9 +911,9 @@ const hi: Translations = {
|
||||||
Schools: 'स्कूल',
|
Schools: 'स्कूल',
|
||||||
'Specific crimes': 'विशिष्ट अपराध',
|
'Specific crimes': 'विशिष्ट अपराध',
|
||||||
Ethnicities: 'जातीय समूह',
|
Ethnicities: 'जातीय समूह',
|
||||||
'POI distance': 'POI दूरी',
|
'Amenity distance': 'सुविधा दूरी',
|
||||||
'POIs within 2km': '2 किमी के अंदर POI',
|
'Amenities within 2km': '2 किमी के अंदर सुविधाएं',
|
||||||
'POIs within 5km': '5 किमी के अंदर POI',
|
'Amenities within 5km': '5 किमी के अंदर सुविधाएं',
|
||||||
Detached: 'अलग मकान',
|
Detached: 'अलग मकान',
|
||||||
'Semi-Detached': 'अर्ध-स्वतंत्र मकान',
|
'Semi-Detached': 'अर्ध-स्वतंत्र मकान',
|
||||||
Terraced: 'कतारबद्ध मकान',
|
Terraced: 'कतारबद्ध मकान',
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ const hu: Translations = {
|
||||||
clearAllSavePrompt: 'Szeretnéd menteni a jelenlegi szűrőket a törlés előtt?',
|
clearAllSavePrompt: 'Szeretnéd menteni a jelenlegi szűrőket a törlés előtt?',
|
||||||
saveAndClear: 'Mentés és törlés',
|
saveAndClear: 'Mentés és törlés',
|
||||||
clearWithoutSaving: 'Törlés mentés nélkül',
|
clearWithoutSaving: 'Törlés mentés nélkül',
|
||||||
withoutThisFilter: '+{{value}} e szűrő nélkül',
|
filtersOut: '{{value}} elemet kiszűr',
|
||||||
schoolType: 'Iskolatípus',
|
schoolType: 'Iskolatípus',
|
||||||
schoolRating: 'Iskolai értékelés',
|
schoolRating: 'Iskolai értékelés',
|
||||||
schoolDistance: 'Iskolatávolság',
|
schoolDistance: 'Iskolatávolság',
|
||||||
|
|
@ -893,7 +893,7 @@ const hu: Translations = {
|
||||||
Properties: 'Ingatlanok',
|
Properties: 'Ingatlanok',
|
||||||
Transport: 'Közlekedés',
|
Transport: 'Közlekedés',
|
||||||
Education: 'Oktatás',
|
Education: 'Oktatás',
|
||||||
'Area characteristics': 'Területi jellemzők',
|
'Area development': 'Területi fejlődés',
|
||||||
Crime: 'Bűnözés',
|
Crime: 'Bűnözés',
|
||||||
Neighbours: 'Szomszédok',
|
Neighbours: 'Szomszédok',
|
||||||
Amenities: 'Szolgáltatások',
|
Amenities: 'Szolgáltatások',
|
||||||
|
|
@ -932,7 +932,7 @@ const hu: Translations = {
|
||||||
'Outstanding secondary schools within 5km': 'Kiemelkedő középiskolák 5 km-en belül',
|
'Outstanding secondary schools within 5km': 'Kiemelkedő középiskolák 5 km-en belül',
|
||||||
'Education, Skills and Training Score': 'Oktatás, készségek és képzés pontszám',
|
'Education, Skills and Training Score': 'Oktatás, készségek és képzés pontszám',
|
||||||
|
|
||||||
// ─ Feature names (Area characteristics) ─
|
// ─ Feature names (Area development) ─
|
||||||
'Income Score': 'Jövedelmi pontszám',
|
'Income Score': 'Jövedelmi pontszám',
|
||||||
'Employment Score': 'Foglalkoztatottsági pontszám',
|
'Employment Score': 'Foglalkoztatottsági pontszám',
|
||||||
'Health Deprivation and Disability Score': 'Egészségügyi depriváció és fogyatékosság pontszám',
|
'Health Deprivation and Disability Score': 'Egészségügyi depriváció és fogyatékosság pontszám',
|
||||||
|
|
@ -986,9 +986,9 @@ const hu: Translations = {
|
||||||
Schools: 'Iskolák',
|
Schools: 'Iskolák',
|
||||||
'Specific crimes': 'Konkrét bűncselekmények',
|
'Specific crimes': 'Konkrét bűncselekmények',
|
||||||
Ethnicities: 'Etnikai csoportok',
|
Ethnicities: 'Etnikai csoportok',
|
||||||
'POI distance': 'POI-távolság',
|
'Amenity distance': 'Szolgáltatás-távolság',
|
||||||
'POIs within 2km': 'POI-k 2 km-en belül',
|
'Amenities within 2km': 'Szolgáltatások 2 km-en belül',
|
||||||
'POIs within 5km': 'POI-k 5 km-en belül',
|
'Amenities within 5km': 'Szolgáltatások 5 km-en belül',
|
||||||
|
|
||||||
// ─ Enum values ─
|
// ─ Enum values ─
|
||||||
Detached: 'Különálló',
|
Detached: 'Különálló',
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,7 @@ const zh: Translations = {
|
||||||
clearAllSavePrompt: '是否要在清除前保存当前的筛选条件?',
|
clearAllSavePrompt: '是否要在清除前保存当前的筛选条件?',
|
||||||
saveAndClear: '保存并清除',
|
saveAndClear: '保存并清除',
|
||||||
clearWithoutSaving: '不保存直接清除',
|
clearWithoutSaving: '不保存直接清除',
|
||||||
withoutThisFilter: '+{{value}} 不使用此筛选条件',
|
filtersOut: '筛除 {{value}}',
|
||||||
schoolType: '学校类型',
|
schoolType: '学校类型',
|
||||||
schoolRating: '学校评级',
|
schoolRating: '学校评级',
|
||||||
schoolDistance: '学校距离',
|
schoolDistance: '学校距离',
|
||||||
|
|
@ -863,7 +863,7 @@ const zh: Translations = {
|
||||||
Properties: '房产',
|
Properties: '房产',
|
||||||
Transport: '交通',
|
Transport: '交通',
|
||||||
Education: '教育',
|
Education: '教育',
|
||||||
'Area characteristics': '区域特征',
|
'Area development': '区域发展',
|
||||||
Crime: '犯罪',
|
Crime: '犯罪',
|
||||||
Neighbours: '邻居',
|
Neighbours: '邻居',
|
||||||
Amenities: '配套设施',
|
Amenities: '配套设施',
|
||||||
|
|
@ -901,7 +901,7 @@ const zh: Translations = {
|
||||||
'Outstanding secondary schools within 5km': '5公里内优秀中学数量',
|
'Outstanding secondary schools within 5km': '5公里内优秀中学数量',
|
||||||
'Education, Skills and Training Score': '教育、技能和培训得分',
|
'Education, Skills and Training Score': '教育、技能和培训得分',
|
||||||
|
|
||||||
// ─ Feature names (Area characteristics) ─
|
// ─ Feature names (Area development) ─
|
||||||
'Income Score': '收入得分',
|
'Income Score': '收入得分',
|
||||||
'Employment Score': '就业得分',
|
'Employment Score': '就业得分',
|
||||||
'Health Deprivation and Disability Score': '健康与残障得分',
|
'Health Deprivation and Disability Score': '健康与残障得分',
|
||||||
|
|
@ -955,9 +955,9 @@ const zh: Translations = {
|
||||||
Schools: '学校',
|
Schools: '学校',
|
||||||
'Specific crimes': '具体犯罪',
|
'Specific crimes': '具体犯罪',
|
||||||
Ethnicities: '族裔',
|
Ethnicities: '族裔',
|
||||||
'POI distance': 'POI 距离',
|
'Amenity distance': '配套设施距离',
|
||||||
'POIs within 2km': '2 公里内 POI',
|
'Amenities within 2km': '2 公里内配套设施',
|
||||||
'POIs within 5km': '5 公里内 POI',
|
'Amenities within 5km': '5 公里内配套设施',
|
||||||
|
|
||||||
// ─ Enum values ─
|
// ─ Enum values ─
|
||||||
Detached: '独立式住宅',
|
Detached: '独立式住宅',
|
||||||
|
|
|
||||||
|
|
@ -21,23 +21,9 @@ import { el, type Storyboard } from './script.js';
|
||||||
* spacing is controlled here via `gapBeforeMs` (silence in audio) plus
|
* spacing is controlled here via `gapBeforeMs` (silence in audio) plus
|
||||||
* optional `tail` activities (visual movement after the caption hides,
|
* optional `tail` activities (visual movement after the caption hides,
|
||||||
* before the next cue's gap).
|
* before the next cue's gap).
|
||||||
*
|
|
||||||
* Sum of `during` declared durations MUST be ≤ measured cue duration. If
|
|
||||||
* synth comes back tighter than the activities can fit, the runner throws
|
|
||||||
* with a pointer to the offending cue — bump that cue's text, lengthen its
|
|
||||||
* gapBefore, or trim a during step.
|
|
||||||
*
|
|
||||||
* Reference durations (Qwen3-TTS / British male narrator, 2026-05-09):
|
|
||||||
* cue 0 1920ms "Describe the life you want."
|
|
||||||
* cue 1 2720ms "Every matching neighbourhood, side by side."
|
|
||||||
* cue 2 2160ms "Tighten the commute to 20 minutes."
|
|
||||||
* cue 3 1840ms "Drill into a single block."
|
|
||||||
* cue 4 4480ms "Stats, listings, Street View, price history…"
|
|
||||||
* cue 5 1760ms "Take the shortlist into Excel."
|
|
||||||
* cue 6 4400ms "Perfect Postcode. Find where you actually want to live."
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const PROMPT_TEXT = 'Flats or terraces <£450k, 35 min to Manchester, low crime';
|
const PROMPT_TEXT = 'Flats <£300k, 35 min to commute Manchester close to an outstanding school in a quite low crime area';
|
||||||
|
|
||||||
const BRAND = {
|
const BRAND = {
|
||||||
name: 'Perfect Postcode',
|
name: 'Perfect Postcode',
|
||||||
|
|
@ -45,59 +31,42 @@ const BRAND = {
|
||||||
url: 'https://perfect-postcode.co.uk',
|
url: 'https://perfect-postcode.co.uk',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cold-open zoom: how aggressively to magnify the AI box.
|
|
||||||
// 2.4 fills most of the viewport with the prompt card without blowing up text.
|
|
||||||
const AI_ZOOM_SCALE = 2.4;
|
const AI_ZOOM_SCALE = 2.4;
|
||||||
|
|
||||||
// The travel-time card we'll drag manually after AI applies. The Filters
|
|
||||||
// component renders each travel-time entry with `data-filter-name="tt_${i}"`,
|
|
||||||
// and our stub only sets one entry, so it's tt_0.
|
|
||||||
const TT_CARD_SELECTOR = '[data-filter-name="tt_0"]';
|
const TT_CARD_SELECTOR = '[data-filter-name="tt_0"]';
|
||||||
const TT_SLIDER_MAX = 120;
|
const TT_SLIDER_MAX = 120;
|
||||||
const TT_DRAG_FROM_MIN = 35; // matches AI stub max below
|
const TT_DRAG_FROM_MIN = 35;
|
||||||
const TT_DRAG_TO_MIN = 20;
|
const TT_DRAG_TO_MIN = 20;
|
||||||
|
|
||||||
// Calm British male narrator. Matches what tts/synth.py used to default to;
|
|
||||||
// kept identical so existing audio caches don't invalidate on first run.
|
|
||||||
const BRITISH_MALE_NARRATOR =
|
const BRITISH_MALE_NARRATOR =
|
||||||
'Calm, professional middle-aged Chinese male narrator with a ' +
|
'Calm but cheerful, professional middle-aged British male narrator from the North with a ' +
|
||||||
'strong Chinese accent. Even, measured pace; warm but ' +
|
'strong Manchester accent. Even, measured pace; warm but and smiling voice; product-demo register. Do not laugh, sigh, gasp, or add ' +
|
||||||
'understated; product-demo register. Do not laugh, sigh, gasp, or add ' +
|
|
||||||
'filler sounds; no audible breaths between sentences.';
|
'filler sounds; no audible breaths between sentences.';
|
||||||
|
|
||||||
const DEFAULT_CUES: Storyboard['cues'] = [
|
const DEFAULT_CUES: Storyboard['cues'] = [
|
||||||
// -- Scene 1: AI prompt ----------------------------------------------
|
|
||||||
// Cue 0 is short (1920ms) — caption shows alone, then typing + submit
|
|
||||||
// happen silently in the tail. The natural beat is: viewer hears the
|
|
||||||
// brief, then watches the prompt being typed.
|
|
||||||
{
|
{
|
||||||
text: 'Describe the life you want.',
|
text: 'Start by describing the type of place you\'re looking for',
|
||||||
gapBeforeMs: 0,
|
gapBeforeMs: 0,
|
||||||
tail: [
|
tail: [
|
||||||
{ kind: 'wait', durationMs: 140 },
|
|
||||||
{
|
{
|
||||||
kind: 'type',
|
kind: 'type',
|
||||||
selector: '[data-tutorial="ai-filters"] textarea',
|
selector: '[data-tutorial="ai-filters"] textarea',
|
||||||
text: PROMPT_TEXT,
|
text: PROMPT_TEXT,
|
||||||
durationMs: 3000,
|
durationMs: 3000,
|
||||||
},
|
},
|
||||||
{ kind: 'wait', durationMs: 140 },
|
|
||||||
{ kind: 'submitForm', formSelector: '[data-tutorial="ai-filters"] form', durationMs: 1700 },
|
{ kind: 'submitForm', formSelector: '[data-tutorial="ai-filters"] form', durationMs: 1700 },
|
||||||
{ kind: 'wait', durationMs: 700 },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
// -- Scene 2: zoom out reveal ---------------------------------------
|
|
||||||
{
|
{
|
||||||
text: 'Every matching neighbourhood, side by side.',
|
text: 'The dashboard will show you the likeliest places that will meet your expectations',
|
||||||
gapBeforeMs: 400,
|
gapBeforeMs: 400,
|
||||||
during: [{ kind: 'zoomReset', durationMs: 1400 }],
|
during: [{ kind: 'zoomReset', durationMs: 1400 }],
|
||||||
tail: [{ kind: 'wait', durationMs: 1200 }],
|
tail: [{ kind: 'wait', durationMs: 500 }],
|
||||||
},
|
},
|
||||||
|
|
||||||
// -- Scene 3: travel-time slider ------------------------------------
|
|
||||||
{
|
{
|
||||||
text: `Tighten the commute to ${TT_DRAG_TO_MIN} minutes.`,
|
text: `Adjust the filters to narrow down to the best candidates`,
|
||||||
gapBeforeMs: 500,
|
gapBeforeMs: 500,
|
||||||
during: [
|
during: [
|
||||||
{
|
{
|
||||||
|
|
@ -105,17 +74,14 @@ const DEFAULT_CUES: Storyboard['cues'] = [
|
||||||
thumbSelector: `${TT_CARD_SELECTOR} [role="slider"] >> nth=1`,
|
thumbSelector: `${TT_CARD_SELECTOR} [role="slider"] >> nth=1`,
|
||||||
trackSelector: `${TT_CARD_SELECTOR} [data-orientation="horizontal"] >> nth=0`,
|
trackSelector: `${TT_CARD_SELECTOR} [data-orientation="horizontal"] >> nth=0`,
|
||||||
toFraction: TT_DRAG_TO_MIN / TT_SLIDER_MAX,
|
toFraction: TT_DRAG_TO_MIN / TT_SLIDER_MAX,
|
||||||
durationMs: 1400,
|
durationMs: 1000,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
tail: [{ kind: 'wait', durationMs: 1200 }],
|
tail: [{ kind: 'wait', durationMs: 400 }],
|
||||||
},
|
},
|
||||||
|
|
||||||
// -- Scene 4a: deep zoom into a hexagon -----------------------------
|
|
||||||
// The mapZoom barely fits (1500ms vs cue 1840ms); cursor prep happens
|
|
||||||
// earlier in this cue's during, the click + payoff dwell are in tail.
|
|
||||||
{
|
{
|
||||||
text: 'Drill into a single block.',
|
text: 'And now it\'s time to dig into the details. Looks good to me!',
|
||||||
gapBeforeMs: 500,
|
gapBeforeMs: 500,
|
||||||
during: [
|
during: [
|
||||||
{ kind: 'cursorScale', scale: 1.4, durationMs: 200 },
|
{ kind: 'cursorScale', scale: 1.4, durationMs: 200 },
|
||||||
|
|
@ -130,7 +96,7 @@ const DEFAULT_CUES: Storyboard['cues'] = [
|
||||||
// Wait for the post-zoom /api/postcodes response and a redraw
|
// Wait for the post-zoom /api/postcodes response and a redraw
|
||||||
// before the click — otherwise the click can fire on a stale
|
// before the click — otherwise the click can fire on a stale
|
||||||
// frame and miss the polygon.
|
// frame and miss the polygon.
|
||||||
{ kind: 'wait', durationMs: 1200 },
|
{ kind: 'wait', durationMs: 500 },
|
||||||
{
|
{
|
||||||
kind: 'click',
|
kind: 'click',
|
||||||
target: { kind: 'point', x: 1140, y: 605 },
|
target: { kind: 'point', x: 1140, y: 605 },
|
||||||
|
|
@ -142,19 +108,8 @@ const DEFAULT_CUES: Storyboard['cues'] = [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
// -- Scene 4b: right-pane payoff -----------------------------------
|
|
||||||
// 4480ms cue, no during — the camera holds on the populated right pane
|
|
||||||
// for the whole climax line. Tail dwells before the export beat.
|
|
||||||
{
|
{
|
||||||
text: 'Stats, listings, Street View, price history — all in one pane.',
|
text: 'Now you can take your shortlist and start looking for your next home in your perfect postcode.',
|
||||||
gapBeforeMs: 0,
|
|
||||||
tail: [{ kind: 'wait', durationMs: 1200 }],
|
|
||||||
},
|
|
||||||
|
|
||||||
// -- Scene 5: export ------------------------------------------------
|
|
||||||
// 1760ms cue. zoomReset + click together fit (1700ms); 60ms padding.
|
|
||||||
{
|
|
||||||
text: 'Take the shortlist into Excel.',
|
|
||||||
gapBeforeMs: 500,
|
gapBeforeMs: 500,
|
||||||
during: [
|
during: [
|
||||||
{ kind: 'zoomReset', durationMs: 900 },
|
{ kind: 'zoomReset', durationMs: 900 },
|
||||||
|
|
@ -167,7 +122,6 @@ const DEFAULT_CUES: Storyboard['cues'] = [
|
||||||
tail: [{ kind: 'wait', durationMs: 800 }],
|
tail: [{ kind: 'wait', durationMs: 800 }],
|
||||||
},
|
},
|
||||||
|
|
||||||
// -- Scene 6: outro -------------------------------------------------
|
|
||||||
{
|
{
|
||||||
text: `${BRAND.name}. ${BRAND.tagline}`,
|
text: `${BRAND.name}. ${BRAND.tagline}`,
|
||||||
gapBeforeMs: 600,
|
gapBeforeMs: 600,
|
||||||
|
|
@ -185,8 +139,6 @@ const DEFAULT_CUES: Storyboard['cues'] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const DEFAULT_PRE: Storyboard['pre'] = [
|
const DEFAULT_PRE: Storyboard['pre'] = [
|
||||||
// Camera push-in to the AI box happens before the first caption — silent
|
|
||||||
// setup keeps the cold open from feeling rushed.
|
|
||||||
{ kind: 'clearVignette', durationMs: 0 },
|
{ kind: 'clearVignette', durationMs: 0 },
|
||||||
{ kind: 'wait', durationMs: 200 },
|
{ kind: 'wait', durationMs: 200 },
|
||||||
{
|
{
|
||||||
|
|
@ -219,10 +171,6 @@ export const storyboards: Storyboard[] = [
|
||||||
voice: {
|
voice: {
|
||||||
instruct: BRITISH_MALE_NARRATOR,
|
instruct: BRITISH_MALE_NARRATOR,
|
||||||
language: 'English',
|
language: 'English',
|
||||||
// Sampling pinned for cue-to-cue consistency. Lower temp/top_p make
|
|
||||||
// the decoder less likely to sample non-speech tokens (laughter,
|
|
||||||
// random noise) at the cost of slightly flatter intonation. Seed
|
|
||||||
// makes runs reproducible.
|
|
||||||
temperature: 0.6,
|
temperature: 0.6,
|
||||||
topP: 0.9,
|
topP: 0.9,
|
||||||
seed: 42,
|
seed: 42,
|
||||||
|
|
@ -230,8 +178,6 @@ export const storyboards: Storyboard[] = [
|
||||||
content: {
|
content: {
|
||||||
promptText: PROMPT_TEXT,
|
promptText: PROMPT_TEXT,
|
||||||
aiZoomScale: AI_ZOOM_SCALE,
|
aiZoomScale: AI_ZOOM_SCALE,
|
||||||
// Initial map view used while we navigate. The AI scene zooms in on
|
|
||||||
// the sidebar so this only matters once we zoom out.
|
|
||||||
initialMapView: { lat: 53.4795, lon: -2.2451, zoom: 11.5 },
|
initialMapView: { lat: 53.4795, lon: -2.2451, zoom: 11.5 },
|
||||||
// Filters returned by the AI stub. Keys MUST match real feature names
|
// Filters returned by the AI stub. Keys MUST match real feature names
|
||||||
// from /api/features (verified against the running server's schema).
|
// from /api/features (verified against the running server's schema).
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue