@jjlmoya/utils-streaming 1.21.0 → 1.22.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.
Files changed (43) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +3 -1
  3. package/src/entries.ts +2 -0
  4. package/src/tests/locale_completeness.test.ts +1 -1
  5. package/src/tests/seo_translation_completeness.test.ts +69 -0
  6. package/src/tests/tool_validation.test.ts +1 -1
  7. package/src/tool/streamSceneCountdownClock/bibliography.astro +6 -0
  8. package/src/tool/streamSceneCountdownClock/bibliography.ts +12 -0
  9. package/src/tool/streamSceneCountdownClock/component.astro +180 -0
  10. package/src/tool/streamSceneCountdownClock/controller.ts +271 -0
  11. package/src/tool/streamSceneCountdownClock/design-state.ts +81 -0
  12. package/src/tool/streamSceneCountdownClock/dom-views.ts +53 -0
  13. package/src/tool/streamSceneCountdownClock/entry.ts +29 -0
  14. package/src/tool/streamSceneCountdownClock/evaluator.ts +28 -0
  15. package/src/tool/streamSceneCountdownClock/i18n/de.ts +47 -0
  16. package/src/tool/streamSceneCountdownClock/i18n/en.ts +253 -0
  17. package/src/tool/streamSceneCountdownClock/i18n/es.ts +47 -0
  18. package/src/tool/streamSceneCountdownClock/i18n/fr.ts +47 -0
  19. package/src/tool/streamSceneCountdownClock/i18n/id.ts +47 -0
  20. package/src/tool/streamSceneCountdownClock/i18n/it.ts +44 -0
  21. package/src/tool/streamSceneCountdownClock/i18n/ja.ts +44 -0
  22. package/src/tool/streamSceneCountdownClock/i18n/ko.ts +44 -0
  23. package/src/tool/streamSceneCountdownClock/i18n/locale-factory.ts +50 -0
  24. package/src/tool/streamSceneCountdownClock/i18n/nl.ts +44 -0
  25. package/src/tool/streamSceneCountdownClock/i18n/pl.ts +44 -0
  26. package/src/tool/streamSceneCountdownClock/i18n/pt.ts +44 -0
  27. package/src/tool/streamSceneCountdownClock/i18n/ru.ts +44 -0
  28. package/src/tool/streamSceneCountdownClock/i18n/sv.ts +44 -0
  29. package/src/tool/streamSceneCountdownClock/i18n/tr.ts +44 -0
  30. package/src/tool/streamSceneCountdownClock/i18n/zh.ts +44 -0
  31. package/src/tool/streamSceneCountdownClock/index.ts +13 -0
  32. package/src/tool/streamSceneCountdownClock/logic.test.ts +51 -0
  33. package/src/tool/streamSceneCountdownClock/logic.ts +108 -0
  34. package/src/tool/streamSceneCountdownClock/seo.astro +15 -0
  35. package/src/tool/streamSceneCountdownClock/settings-state.ts +24 -0
  36. package/src/tool/streamSceneCountdownClock/storage.ts +32 -0
  37. package/src/tool/streamSceneCountdownClock/stream-scene-countdown-clock.css +1745 -0
  38. package/src/tool/streamSceneCountdownClock/stream-url-view.ts +32 -0
  39. package/src/tool/streamSceneCountdownClock/text-input-state.ts +31 -0
  40. package/src/tool/streamSceneCountdownClock/ui.ts +69 -0
  41. package/src/tool/streamSceneCountdownClock/url-state.test.ts +48 -0
  42. package/src/tool/streamSceneCountdownClock/url-state.ts +118 -0
  43. package/src/tools.ts +2 -0
@@ -0,0 +1,81 @@
1
+ import type { SavedClockSettings } from './storage';
2
+ import type { DesignKey } from './ui';
3
+
4
+ export const designPalettes: Record<DesignKey, Pick<SavedClockSettings, 'accentColor' | 'glowColor'>> = {
5
+ aurora: { accentColor: '#a78bfa', glowColor: '#8ff5d7' },
6
+ type: { accentColor: '#ffb84d', glowColor: '#ff5f91' },
7
+ pulse: { accentColor: '#ff6b9d', glowColor: '#9d7bff' },
8
+ glitch: { accentColor: '#d8ff3e', glowColor: '#5ee7ff' },
9
+ sunset: { accentColor: '#ff6b4a', glowColor: '#ffc36b' },
10
+ };
11
+
12
+ export function renderDesignChoices(root: HTMLElement, design: DesignKey): void {
13
+ root.querySelectorAll<HTMLButtonElement>('[data-preview-design]').forEach((button) => {
14
+ const active = button.dataset.previewDesign === design;
15
+ button.classList.toggle('is-active', active);
16
+ button.setAttribute('aria-selected', String(active));
17
+ });
18
+ }
19
+
20
+ export function renderDesignSelect(root: HTMLElement, design: DesignKey): void {
21
+ const trigger = root.querySelector<HTMLButtonElement>('[data-design-trigger]');
22
+ const selected = root.querySelector<HTMLButtonElement>(`[data-design-option="${design}"]`);
23
+ if (trigger && selected) trigger.textContent = selected.textContent?.trim() ?? '';
24
+ root.querySelectorAll<HTMLButtonElement>('[data-design-option]').forEach((option) => {
25
+ const active = option.dataset.designOption === design;
26
+ option.classList.toggle('is-active', active);
27
+ option.setAttribute('aria-selected', String(active));
28
+ });
29
+ }
30
+
31
+ export function bindDesignSelect(root: HTMLElement, onSelect: (design: DesignKey) => void): void {
32
+ const trigger = root.querySelector<HTMLButtonElement>('[data-design-trigger]');
33
+ const options = Array.from(root.querySelectorAll<HTMLButtonElement>('[data-design-option]'));
34
+ const menu = root.querySelector<HTMLElement>('[data-design-options]');
35
+ if (!trigger || !menu) return;
36
+ const close = () => {
37
+ menu.hidden = true;
38
+ trigger.setAttribute('aria-expanded', 'false');
39
+ };
40
+ const open = () => {
41
+ menu.hidden = false;
42
+ trigger.setAttribute('aria-expanded', 'true');
43
+ };
44
+ trigger.addEventListener('click', () => {
45
+ if (menu.hidden) open();
46
+ else close();
47
+ });
48
+ options.forEach((option) => option.addEventListener('click', () => {
49
+ onSelect(option.dataset.designOption as DesignKey);
50
+ close();
51
+ }));
52
+ trigger.addEventListener('keydown', (event) => handleSelectKey(event, options, open, close));
53
+ document.addEventListener('click', (event) => {
54
+ if (!root.contains(event.target as Node)) close();
55
+ });
56
+ }
57
+
58
+ function handleSelectKey(event: KeyboardEvent, options: HTMLButtonElement[], open: () => void, close: () => void): void {
59
+ if (event.key === 'Escape') {
60
+ close();
61
+ return;
62
+ }
63
+ if (event.key === 'Enter' || event.key === ' ') {
64
+ event.preventDefault();
65
+ open();
66
+ return;
67
+ }
68
+ if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return;
69
+ event.preventDefault();
70
+ open();
71
+ const selectedIndex = options.findIndex((option) => option.getAttribute('aria-selected') === 'true');
72
+ const direction = event.key === 'ArrowDown' ? 1 : -1;
73
+ const nextIndex = (selectedIndex + direction + options.length) % options.length;
74
+ options[nextIndex]?.focus();
75
+ }
76
+
77
+ export function bindPreviewDesignButtons(root: HTMLElement, onSelect: (design: DesignKey) => void): void {
78
+ root.querySelectorAll<HTMLButtonElement>('[data-preview-design]').forEach((button) => {
79
+ button.addEventListener('click', () => onSelect(button.dataset.previewDesign as DesignKey));
80
+ });
81
+ }
@@ -0,0 +1,53 @@
1
+ import { formatCountdown, formatLocalTime, type CountdownSnapshot } from './logic';
2
+ import { evaluateStatus } from './evaluator';
3
+ import type { SceneKey, StreamSceneCountdownClockUI } from './ui';
4
+
5
+ interface RenderInput {
6
+ root: HTMLElement;
7
+ snapshot: CountdownSnapshot;
8
+ scene: SceneKey;
9
+ sceneTitle: string;
10
+ message: string;
11
+ ui: StreamSceneCountdownClockUI;
12
+ }
13
+
14
+ function setText(root: HTMLElement, selector: string, value: string): void {
15
+ const element = root.querySelector<HTMLElement>(selector);
16
+ if (element) element.textContent = value;
17
+ }
18
+
19
+ export function renderClock(input: RenderInput): void {
20
+ const { root, snapshot, scene, ui } = input;
21
+ const reading = evaluateStatus(snapshot.status, ui);
22
+ const stageMessage = input.message.trim() || ui.stageCaption;
23
+ root.dataset.status = reading.tone;
24
+ root.style.setProperty('--n-progress', String(snapshot.elapsedFraction));
25
+ const clock = root.querySelector<HTMLElement>('[data-clock]');
26
+ if (clock) {
27
+ const parts = formatCountdown(snapshot.displaySeconds).split(':');
28
+ clock.innerHTML = parts.map((part, index) => `${index > 0 ? '<span class="ssc-clock-separator">:</span>' : ''}<span class="ssc-clock-part">${part}</span>`).join('');
29
+ }
30
+ setText(root, '[data-stage-message]', stageMessage);
31
+ setText(root, '[data-status-badge]', reading.badge);
32
+ setText(root, '[data-status-copy]', reading.text);
33
+ setText(root, '[data-scene-name]', input.sceneTitle.trim() || getSceneLabel(scene, ui));
34
+ setText(root, '[data-start-time]', formatLocalTime(snapshot.startAtMs));
35
+ setText(root, '[data-end-time]', formatLocalTime(snapshot.endAtMs));
36
+ setText(root, '[data-progress-copy]', `${Math.round(snapshot.elapsedFraction * 100)}%`);
37
+ const progress = root.querySelector<SVGCircleElement>('[data-progress-ring]');
38
+ if (progress) progress.style.strokeDashoffset = String(314.16 * (1 - snapshot.elapsedFraction));
39
+ const altProgress = root.querySelector<HTMLElement>('[data-alt-progress]');
40
+ if (altProgress) altProgress.style.transform = `scaleX(${snapshot.elapsedFraction})`;
41
+ const badge = root.querySelector<HTMLElement>('[data-status-badge]');
42
+ if (badge) badge.setAttribute('aria-label', `${ui.statusAria}: ${reading.badge}`);
43
+ }
44
+
45
+ export function getSceneLabel(scene: SceneKey, ui: StreamSceneCountdownClockUI): string {
46
+ const labels: Record<SceneKey, string> = {
47
+ brb: ui.sceneBrb,
48
+ starting: ui.sceneStarting,
49
+ raid: ui.sceneRaid,
50
+ intermission: ui.sceneIntermission,
51
+ };
52
+ return labels[scene];
53
+ }
@@ -0,0 +1,29 @@
1
+ import type { StreamingToolEntry, ToolLocaleContent } from '../../types';
2
+ import type { StreamSceneCountdownClockUI } from './ui';
3
+
4
+ export type StreamSceneCountdownClockLocaleContent = ToolLocaleContent<StreamSceneCountdownClockUI>;
5
+
6
+ export const streamSceneCountdownClock: StreamingToolEntry<StreamSceneCountdownClockUI> = {
7
+ id: 'streamSceneCountdownClock',
8
+ icons: {
9
+ bg: 'mdi:television-ambient-light',
10
+ fg: 'mdi:timer-outline',
11
+ },
12
+ i18n: {
13
+ en: () => import('./i18n/en').then((m) => m.content),
14
+ de: () => import('./i18n/de').then((m) => m.content),
15
+ es: () => import('./i18n/es').then((m) => m.content),
16
+ fr: () => import('./i18n/fr').then((m) => m.content),
17
+ id: () => import('./i18n/id').then((m) => m.content),
18
+ it: () => import('./i18n/it').then((m) => m.content),
19
+ ja: () => import('./i18n/ja').then((m) => m.content),
20
+ ko: () => import('./i18n/ko').then((m) => m.content),
21
+ nl: () => import('./i18n/nl').then((m) => m.content),
22
+ pl: () => import('./i18n/pl').then((m) => m.content),
23
+ pt: () => import('./i18n/pt').then((m) => m.content),
24
+ ru: () => import('./i18n/ru').then((m) => m.content),
25
+ sv: () => import('./i18n/sv').then((m) => m.content),
26
+ tr: () => import('./i18n/tr').then((m) => m.content),
27
+ zh: () => import('./i18n/zh').then((m) => m.content),
28
+ },
29
+ };
@@ -0,0 +1,28 @@
1
+ import type { ClockStatus } from './logic';
2
+
3
+ export interface StatusLabels {
4
+ readyBadge: string;
5
+ waitingBadge: string;
6
+ liveBadge: string;
7
+ endedBadge: string;
8
+ readyText: string;
9
+ waitingText: string;
10
+ liveText: string;
11
+ endedText: string;
12
+ }
13
+
14
+ export interface StatusReading {
15
+ badge: string;
16
+ text: string;
17
+ tone: ClockStatus;
18
+ }
19
+
20
+ export function evaluateStatus(status: ClockStatus, labels: StatusLabels): StatusReading {
21
+ const readings: Record<ClockStatus, StatusReading> = {
22
+ ready: { badge: labels.readyBadge, text: labels.readyText, tone: 'ready' },
23
+ waiting: { badge: labels.waitingBadge, text: labels.waitingText, tone: 'waiting' },
24
+ live: { badge: labels.liveBadge, text: labels.liveText, tone: 'live' },
25
+ ended: { badge: labels.endedBadge, text: labels.endedText, tone: 'ended' },
26
+ };
27
+ return readings[status];
28
+ }
@@ -0,0 +1,47 @@
1
+ import { makeContent } from './locale-factory';
2
+
3
+ export const content = makeContent({
4
+ language: 'de',
5
+ slug: 'stream-countdown-uhr-fuer-obs',
6
+ title: 'Countdown Uhr fuer Stream Szenen',
7
+ description: 'Erstelle eine klare Countdown Szene fuer Starting soon, BRB, Raids und Unterbrechungen in deinem Stream.',
8
+ ui: {
9
+ sceneLabel: 'Welche Szene richtest du ein?', sceneBrb: 'BRB', sceneStarting: 'Gleich geht es los', sceneRaid: 'Raid', sceneIntermission: 'Pause',
10
+ sceneTitleLabel: 'Was soll über dem Timer stehen?', sceneTitlePlaceholder: 'Gleich geht es los', designLabel: 'Wie soll sich deine Szene anfühlen?', designAurora: 'Polarlicht Schleier', designType: 'Kinetische Schrift', designPulse: 'Pulsschimmer', designGlitch: 'Glitch Signal', designSunset: 'Sonnenflare',
11
+ accentColorLabel: 'Akzentfarbe', glowColorLabel: 'Leuchtfarbe', messageLabel: 'Was sollen deine Zuschauer wissen?', messagePlaceholder: 'Bin in 5 Minuten zurück', durationLabel: 'Wie viel Zeit brauchst du?', duration60: '1 Min.', duration300: '5 Min.', duration600: '10 Min.', durationCustom: 'Benutzerdefiniert', secondsLabel: 'Sekunden', startLabel: 'Wann soll dieser Hinweis starten?', startNow: 'Jetzt starten', scheduleTime: 'Zeit planen', timeLabel: 'Zu welcher Ortszeit soll er starten?',
12
+ startAction: 'Hinweis auf Sendung setzen', focusAction: 'Meine Szene zeigen', exitFocusAction: 'Szenenansicht verlassen', resetAction: 'Hinweis zurücksetzen', flowText: 'Du bist der Streamer: Waehle den Moment, stelle deine Zeit ein und schalte den Hinweis auf Sendung.', obsTitle: 'Diese Szene in OBS verwenden', obsText: 'Kopiere den Link, fuege eine OBS Browserquelle hinzu, setze den Link ein und nutze deine Ausgabeaufloesung. STREAMING oeffnet die saubere Vollbildszene automatisch.', obsStepCopy: 'Diesen Link kopieren', obsStepAdd: 'Eine Browserquelle in OBS hinzufuegen', obsStepPaste: 'Link einfuegen und Leinwandgroesse angleichen', copyUrlAction: 'OBS Link kopieren', copiedUrlText: 'OBS Link kopiert', streamUrlAria: 'Erzeugte OBS Streaming URL', previewTitle: 'Szenenlook waehlen', previewHint: 'Klicke auf eine Vorschau, um sie zu verwenden', previewAria: 'Vorschauen der Szenenlooks', stageEyebrow: 'Sendungsbild', stageCaption: 'Deine naechste Szene ist bereit', readyBadge: 'Bereit', waitingBadge: 'Wartet', liveBadge: 'Live Hinweis', endedBadge: 'Beendet', readyText: 'Pruefe die Szene und starte den Hinweis, sobald deine Szene bereit ist.', waitingText: 'Der Live Countdown beginnt zur geplanten Ortszeit.', liveText: 'Lass diese Szene sichtbar, bis der Wechsel bereit ist.', endedText: 'Der Hinweis ist vorbei. Setze ihn zurück oder richte eine neue Szene ein.', remainingLabel: 'Zeit in der Szene', startTimeLabel: 'Start', endTimeLabel: 'Ende', progressLabel: 'Szenenfortschritt', assumptionTitle: 'Hinweis zur Zeitmessung', assumptionText: 'Geplante Zeiten verwenden die Uhr deines Geraets. Der Timer ist ein visueller Hinweis und synchronisiert OBS, Twitch, Chat oder Encoder nicht.', warningTitle: 'Als Szenenhinweis verwenden', warningText: 'Ein schlafender Tab, eine geaenderte Systemzeit oder eine verzoegerte Sendung kann die sichtbare Zeit von deinem Stream abweichen lassen. Pruefe die Live Szene vor dem Wechsel.', invalidTime: 'Gib eine Ortszeit im Format HH:MM ein.', clockAria: 'Verbleibende Countdown Zeit', statusAria: 'Countdown Status',
13
+ },
14
+ faq: [
15
+ { question: 'Verbindet sich diese Uhr mit OBS oder Twitch?', answer: 'Nein. Sie ist ein eigenstaendiger Timer und steuert weder OBS, Twitch, Chat noch einen Streamingserver. Nutze Meine Szene zeigen fuer eine bildschirmfuellende Ansicht und erfasse sie als Browserquelle.' },
16
+ { question: 'Wie verwende ich den Countdown direkt in OBS?', answer: 'Fuege die Tool URL als OBS Browserquelle mit dem Streaming Schalter und den Einstellungen hinzu, zum Beispiel ?STREAMING&scene=starting&duration=300&design=aurora&title=Gleich%20geht%20es%20los. Steuerelemente verschwinden und der Countdown fuellt den Browserquellenbereich.' },
17
+ { question: 'Was passiert bei einer geplanten Startzeit?', answer: 'Die Uhr wartet bis zur gewaehlten Ortszeit und zaehlt dann die Dauer der Szene herunter. Eine bereits vergangene Zeit gilt als naechster Termin am folgenden Tag.' },
18
+ { question: 'Kann ich eine eigene Nachricht verwenden?', answer: 'Ja. Schreibe als Streamer den kurzen Hinweis fuer deine Zuschauer, etwa eine Rueckkehrzeit, den naechsten Schritt oder eine Raid Nachricht.' },
19
+ { question: 'Was aendern die Szenenpresets?', answer: 'Sie benennen den Moment der Sendung, damit die Szene auf einen Blick erkennbar ist. Timerrechnung, Dauer und Verbindung zu einer Plattform bleiben unveraendert.' },
20
+ { question: 'Ist die Endzeit exakt?', answer: 'Sie wird aus der lokalen Geraeteuhr und deiner Dauer berechnet. Ruhezustand des Browsers, pausierte Tabs oder eine geaenderte Uhr koennen die sichtbare Aktualisierung beeinflussen. Sie ist daher ein Planungshinweis, keine Sendesynchronisation.' },
21
+ ],
22
+ howTo: [
23
+ { name: 'Szenenmoment waehlen', text: 'Waehle Starting soon, BRB, Raid oder Pause, damit die Szene den Moment fuer deine Zuschauer benennt.' },
24
+ { name: 'Streamer Hinweis schreiben', text: 'Gib die kurze Nachricht ein, die deine Zuschauer beim Vorbereiten der naechsten Szene lesen sollen.' },
25
+ { name: 'Dauer oder Startzeit einstellen', text: 'Waehle eine haeufige Dauer oder gib deine eigene ein. Starte sofort fuer einen Live Hinweis oder plane eine lokale Uhrzeit.' },
26
+ { name: 'Status lesen', text: 'Nutze die grosse Uhr und das Statuslabel, um zu entscheiden, wann du die Sendeszene wechselst.' },
27
+ ],
28
+ seo: [
29
+ { type: 'title', text: 'Eine Stream Szene auf einen Blick vorbereiten', level: 2 },
30
+ { type: 'paragraph', html: 'Eine Countdown Uhr gibt deiner Starting soon, BRB, Raid oder Pausenszene einen klaren Rueckkehrpunkt. Schreibe, was du tust, stelle deine Zeit ein und nutze die grosse Uhr waehrend der Vorbereitung.' },
31
+ { type: 'title', text: 'Was die Szenenuhr berechnet', level: 3 },
32
+ { type: 'list', items: ['<strong>Sofortiger Hinweis:</strong> startet beim Auf Sendung Schalten und zaehlt die gewaehlte Dauer herunter.', '<strong>Geplanter Hinweis:</strong> wartet auf deine Ortszeit und beginnt danach mit der Szenendauer.', '<strong>Szenenstatus:</strong> trennt bereit, wartend, live und beendet, damit die naechste Aktion sichtbar bleibt.'] },
33
+ { type: 'title', text: 'Die passende Szenendauer waehlen', level: 3 },
34
+ { type: 'paragraph', html: 'Nimm eine kurze Dauer fuer Quellenwechsel oder eine schnelle Unterbrechung. Eine längere Dauer hilft bei Gaesten, einem Spielstart oder einem technischen Neustart. Deine Nachricht sollte Informationen liefern, die die Uhr allein nicht zeigt.' },
35
+ { type: 'tip', title: 'Die Rueckkehr konkret machen', html: 'Schreibe statt eines allgemeinen Versprechens die naechste Aktion: "Um 20:30 mit dem Finale zurück" oder "Raid wird vorbereitet". So sehen Zuschauer Wartezeit und Grund.' },
36
+ { type: 'title', text: 'Warum die Uhr offline bleibt', level: 3 },
37
+ { type: 'paragraph', html: 'Die Uhr braucht keinen Zugriff auf deinen Kanal oder deine Sendesoftware. Text und Zeit bleiben lokal und eignen sich als Planungsszene. Pruefe vor dem Livegang trotzdem die Sichtbarkeit der Browserquelle und den Szenenwechsel in deiner Sendesoftware.' },
38
+ { type: 'title', text: 'Die fertige Szene an OBS senden', level: 3 },
39
+ { type: 'paragraph', html: 'Klicke auf <strong>OBS Link kopieren</strong>, fuege in deiner Streamszene eine Browserquelle hinzu, setze den Link ein und gleiche die Leinwandaufloesung an. <code>?STREAMING</code> oeffnet eine saubere, selbststartende Vollbildszene ohne Bedienelemente.' },
40
+ { type: 'title', text: 'Den Look an deinen Stream anpassen', level: 3 },
41
+ { type: 'paragraph', html: 'Waehle zwischen fuenf unterschiedlichen Richtungen: Polarlicht Schleier fuer den atmosphaerischen Ring, Kinetische Schrift fuer grosse Zahlen, Pulsschimmer fuer expandierende Wellen, Glitch Signal fuer scharfe Broadcast Energie und Sonnenflare fuer einen warmen Horizont. Waehle eine Vorschau und passe Akzent und Leuchtfarbe an.' },
42
+ { type: 'list', items: ['<strong>Szene:</strong> benennt Starting soon, BRB, Raid oder Pause.', '<strong>Titel:</strong> ersetzt die Standardszeile durch deine eigene Ueberschrift.', '<strong>Nachricht:</strong> gibt den zusaetzlichen Hinweis fuer Zuschauer aus.', '<strong>Dauer und Startzeit:</strong> bestimmen Beginn und sichtbare Laufzeit.'] },
43
+ { type: 'paragraph', html: 'Wenn du den Link selbst baust, reicht <code>?STREAMING&amp;scene=raid&amp;title=Raid%20startet&amp;design=pulse</code>. Der Generator fuegt Dauer, Nachricht und Farben automatisch hinzu.' },
44
+ { type: 'title', text: 'Die Endzeit als Planungshinweis lesen', level: 3 },
45
+ { type: 'paragraph', html: 'Die Endzeit kommt aus deiner Geraeteuhr und der gewaehlten Dauer. Sie garantiert nicht, dass der Stream im selben Moment bei allen Zuschauern ankommt. Plane einen kleinen Puffer fuer Szenenwechsel mit Gaesten, Verbindungen oder Uebergaengen ein.' },
46
+ ],
47
+ });
@@ -0,0 +1,253 @@
1
+ import { bibliography } from '../bibliography';
2
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
3
+ import type { ToolLocaleContent } from '../../../types';
4
+ import type { StreamSceneCountdownClockUI } from '../ui';
5
+
6
+ const slug = 'stream-scene-countdown-clock';
7
+ const title = 'Stream Scene Countdown Clock';
8
+ const description = 'Build a streamer-first countdown slate for starting soon, BRB, raid, and intermission scenes.';
9
+
10
+ const faq = [
11
+ {
12
+ question: 'Does this countdown connect to OBS or Twitch?',
13
+ answer: 'No. It is a standalone timer and does not control OBS, Twitch, chat, or a stream server. Use Focus slate to make the countdown fill the screen, then capture that view as a browser source.',
14
+ },
15
+ {
16
+ question: 'How do I use the countdown directly in OBS?',
17
+ answer: 'Add the tool URL as an OBS Browser Source with the streaming flag and inputs, for example ?STREAMING&scene=starting&duration=300&design=aurora&title=Starting%20soon&accent=%23a78bfa&glow=%238ff5d7&message=Back%20soon. The controls and page chrome disappear, and the countdown fills the browser source viewport.',
18
+ },
19
+ {
20
+ question: 'What happens when I schedule a start time?',
21
+ answer: 'The clock enters a waiting state until the selected local time, then counts down the scene duration. A time that has already passed is treated as the next occurrence on the following day.',
22
+ },
23
+ {
24
+ question: 'Can I use a custom message?',
25
+ answer: 'Yes. As the streamer, write the short cue you want viewers to see, such as a return time, a next step, or a raid message.',
26
+ },
27
+ {
28
+ question: 'What do the scene presets change?',
29
+ answer: 'They label the broadcast moment so the slate is easier to recognize at a glance. The preset does not change the timer math, duration, or connection to a streaming platform.',
30
+ },
31
+ {
32
+ question: 'Is the end time exact?',
33
+ answer: 'It is calculated from the local browser clock and the duration you enter. Browser sleep, a paused tab, or a clock change can affect when the display visibly refreshes, so it is a planning cue rather than a broadcast synchronization guarantee.',
34
+ },
35
+ ];
36
+
37
+ const howTo = [
38
+ {
39
+ name: 'Choose the scene moment',
40
+ text: 'Select Starting soon, BRB, Raid, or Intermission so the slate names the moment your viewers are seeing.',
41
+ },
42
+ {
43
+ name: 'Write your streamer cue',
44
+ text: 'Enter the short message you want viewers to read while you prepare the next scene, such as when you will return or what happens next.',
45
+ },
46
+ {
47
+ name: 'Set the duration or start time',
48
+ text: 'Pick a common duration or enter your own. Start immediately for a live cue, or schedule a local clock time for a planned scene.',
49
+ },
50
+ {
51
+ name: 'Read the status',
52
+ text: 'Use the large clock and status badge to decide when to switch your broadcast scene.',
53
+ },
54
+ ];
55
+
56
+ const faqSchema: WithContext<FAQPage> = {
57
+ '@context': 'https://schema.org',
58
+ '@type': 'FAQPage',
59
+ mainEntity: faq.map((item) => ({
60
+ '@type': 'Question',
61
+ name: item.question,
62
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
63
+ })),
64
+ };
65
+
66
+ const howToSchema: WithContext<HowTo> = {
67
+ '@context': 'https://schema.org',
68
+ '@type': 'HowTo',
69
+ name: title,
70
+ description,
71
+ step: howTo.map((step, index) => ({
72
+ '@type': 'HowToStep',
73
+ position: index + 1,
74
+ name: step.name,
75
+ text: step.text,
76
+ })),
77
+ };
78
+
79
+ const appSchema: WithContext<SoftwareApplication> = {
80
+ '@context': 'https://schema.org',
81
+ '@type': 'SoftwareApplication',
82
+ name: title,
83
+ description,
84
+ applicationCategory: 'UtilityApplication',
85
+ operatingSystem: 'All',
86
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
87
+ inLanguage: 'en',
88
+ };
89
+
90
+ const ui: StreamSceneCountdownClockUI = {
91
+ sceneLabel: 'Which scene are you setting up?',
92
+ sceneBrb: 'BRB',
93
+ sceneStarting: 'Starting soon',
94
+ sceneRaid: 'Raid',
95
+ sceneIntermission: 'Intermission',
96
+ sceneTitleLabel: 'What should appear above the timer?',
97
+ sceneTitlePlaceholder: 'Starting soon',
98
+ designLabel: 'What should your scene feel like?',
99
+ designAurora: 'Aurora haze',
100
+ designType: 'Kinetic type',
101
+ designPulse: 'Pulse bloom',
102
+ designGlitch: 'Glitch signal',
103
+ designSunset: 'Solar flare',
104
+ accentColorLabel: 'Accent color',
105
+ glowColorLabel: 'Glow color',
106
+ messageLabel: 'What should your viewers know?',
107
+ messagePlaceholder: 'Back in 5 minutes',
108
+ durationLabel: 'How long do you need?',
109
+ duration60: '1 min',
110
+ duration300: '5 min',
111
+ duration600: '10 min',
112
+ durationCustom: 'Custom',
113
+ secondsLabel: 'seconds',
114
+ startLabel: 'When should this cue start?',
115
+ startNow: 'Start now',
116
+ scheduleTime: 'Schedule a time',
117
+ timeLabel: 'What local time should it start?',
118
+ startAction: 'Put my cue on air',
119
+ focusAction: 'Show my slate',
120
+ exitFocusAction: 'Exit slate view',
121
+ resetAction: 'Reset my cue',
122
+ flowText: 'You are the streamer: choose what is happening, set the time you need, then put your cue on air.',
123
+ obsTitle: 'Put this scene in OBS',
124
+ obsText: 'Copy the link, add an OBS Browser Source, paste it into the URL field, and use your scene resolution. STREAMING opens the clean full-screen slate automatically.',
125
+ obsStepCopy: 'Copy this link',
126
+ obsStepAdd: 'Add a Browser Source in OBS',
127
+ obsStepPaste: 'Paste it and match your canvas size',
128
+ copyUrlAction: 'Copy OBS link',
129
+ copiedUrlText: 'OBS link copied',
130
+ streamUrlAria: 'Generated OBS streaming URL',
131
+ previewTitle: 'Choose your scene look',
132
+ previewHint: 'Click a preview to use it',
133
+ previewAria: 'Scene design previews',
134
+ stageEyebrow: 'Broadcast slate',
135
+ stageCaption: 'Your next scene is ready',
136
+ readyBadge: 'Ready',
137
+ waitingBadge: 'Waiting',
138
+ liveBadge: 'Live cue',
139
+ endedBadge: 'Ended',
140
+ readyText: 'Preview the slate, then start the cue when your scene is ready.',
141
+ waitingText: 'The slate will switch to its live countdown at the scheduled local time.',
142
+ liveText: 'Keep this slate visible until the scene change is ready.',
143
+ endedText: 'The planned cue is over. Reset it or set a new scene.',
144
+ remainingLabel: 'Time on slate',
145
+ startTimeLabel: 'Starts',
146
+ endTimeLabel: 'Ends',
147
+ progressLabel: 'Scene progress',
148
+ assumptionTitle: 'Timing note',
149
+ assumptionText: 'Scheduled times use your device clock. The timer is a visual cue and does not synchronize OBS, Twitch, chat, or an encoder.',
150
+ warningTitle: 'Use it as a scene cue',
151
+ warningText: 'A sleeping browser tab, a changed system clock, or a delayed broadcast can make the visible time differ from the actual stream. Check the live scene before switching.',
152
+ invalidTime: 'Enter a local time in the format HH:MM.',
153
+ clockAria: 'Countdown time remaining',
154
+ statusAria: 'Countdown status',
155
+ };
156
+
157
+ export const content: ToolLocaleContent<StreamSceneCountdownClockUI> = {
158
+ slug,
159
+ title,
160
+ description,
161
+ ui,
162
+ faq,
163
+ bibliography,
164
+ howTo,
165
+ schemas: [faqSchema as any, howToSchema as any, appSchema as any],
166
+ seo: [
167
+ {
168
+ type: 'title',
169
+ text: 'Keep a stream scene cue readable at a glance',
170
+ level: 2,
171
+ },
172
+ {
173
+ type: 'paragraph',
174
+ html: 'A stream countdown clock gives your starting soon, BRB, raid, or intermission scene a clear return point. Tell viewers what you are doing, set the time you need, and use the large timer while you prepare the broadcast.',
175
+ },
176
+ {
177
+ type: 'title',
178
+ text: 'What the scene clock calculates',
179
+ level: 3,
180
+ },
181
+ {
182
+ type: 'list',
183
+ items: [
184
+ '<strong>Immediate cue:</strong> starts from the moment you put the clock on air and counts down the selected duration.',
185
+ '<strong>Scheduled cue:</strong> waits for the local time you enter, then begins the scene duration.',
186
+ '<strong>Scene status:</strong> separates ready, waiting, live, and ended so the display communicates what action is next.',
187
+ ],
188
+ },
189
+ {
190
+ type: 'title',
191
+ text: 'How to choose a useful scene duration',
192
+ level: 3,
193
+ },
194
+ {
195
+ type: 'paragraph',
196
+ html: 'Use a short duration when you are changing sources or returning from a quick interruption. Choose a longer duration when you need time to prepare a guest, a game, or a technical reset. Your cue should add information that the timer alone cannot provide.',
197
+ },
198
+ {
199
+ type: 'tip',
200
+ title: 'Make the return cue specific',
201
+ html: 'Instead of a generic promise, write the next action: "Back at 8:30 with the final match" or "Raid setup in progress". Your cue then gives viewers the wait and the reason for it.',
202
+ },
203
+ {
204
+ type: 'title',
205
+ text: 'Why the clock is intentionally offline',
206
+ level: 3,
207
+ },
208
+ {
209
+ type: 'paragraph',
210
+ html: 'The clock does not need access to your channel or broadcast software. Keeping the scene text and timing local makes it useful as a planning slate and avoids sending private production notes to a third party. If you use a browser source, verify the source visibility and scene transition in your broadcast software before going live.',
211
+ },
212
+ {
213
+ type: 'title',
214
+ text: 'Send a finished scene to OBS',
215
+ level: 3,
216
+ },
217
+ {
218
+ type: 'paragraph',
219
+ html: 'Your scene is ready when the preview looks right. Click <strong>Copy OBS link</strong>, add a Browser Source to the scene you use for your stream, paste the link, and match your canvas resolution. The <code>?STREAMING</code> flag opens a clean, self-starting slate with the controls removed.',
220
+ },
221
+ {
222
+ type: 'title',
223
+ text: 'Make the slate look like your stream',
224
+ level: 3,
225
+ },
226
+ {
227
+ type: 'paragraph',
228
+ html: 'Choose a visual direction from five genuinely different layouts: Aurora haze keeps the atmospheric ring, Kinetic type builds the countdown from oversized numbers, Pulse bloom uses expanding waves, Glitch signal brings sharp broadcast energy, and Solar flare creates a warm horizon. Pick a preview before you open the output, then tune the accent and glow colors to your channel.',
229
+ },
230
+ {
231
+ type: 'list',
232
+ items: [
233
+ '<strong>Scene:</strong> labels the moment - starting soon, BRB, raid, or intermission.',
234
+ '<strong>Title:</strong> replaces the default scene line with your own headline, such as "FINAL BOSS FIGHT".',
235
+ '<strong>Message:</strong> adds the supporting cue your viewers need to read.',
236
+ '<strong>Duration and start time:</strong> control when the scene begins and how long it stays live.',
237
+ ],
238
+ },
239
+ {
240
+ type: 'paragraph',
241
+ html: 'If you build the URL yourself, keep it short with <code>?STREAMING&amp;scene=raid&amp;title=Raid%20starts%20soon&amp;design=pulse</code>. The generator includes your duration, message, and custom colors automatically, so you can paste one complete link into OBS.',
242
+ },
243
+ {
244
+ type: 'title',
245
+ text: 'Read the end time as a planning cue',
246
+ level: 3,
247
+ },
248
+ {
249
+ type: 'paragraph',
250
+ html: 'The end time is derived from your device clock and the duration you chose. It is not a guarantee that the stream reaches viewers at the same instant. Keep a small buffer when the scene change depends on another person, a guest connection, or a broadcast transition.',
251
+ },
252
+ ],
253
+ };
@@ -0,0 +1,47 @@
1
+ import { makeContent } from './locale-factory';
2
+
3
+ export const content = makeContent({
4
+ language: 'es',
5
+ slug: 'cuenta-atras-escena-streaming-obs',
6
+ title: 'Cuenta atrás de escenas para streaming',
7
+ description: 'Crea una escena de cuenta atrás pensada para streamers: empezamos pronto, BRB, raid e intermedio.',
8
+ ui: {
9
+ sceneLabel: '¿Qué escena estás preparando?', sceneBrb: 'BRB', sceneStarting: 'Empezamos pronto', sceneRaid: 'Raid', sceneIntermission: 'Intermedio',
10
+ sceneTitleLabel: '¿Qué debe aparecer sobre el contador?', sceneTitlePlaceholder: 'Empezamos pronto', designLabel: '¿Qué ambiente debe tener tu escena?', designAurora: 'Bruma aurora', designType: 'Tipografía cinética', designPulse: 'Pulso expansivo', designGlitch: 'Señal glitch', designSunset: 'Destello solar',
11
+ accentColorLabel: 'Color de acento', glowColorLabel: 'Color del brillo', messageLabel: '¿Qué deben saber tus espectadores?', messagePlaceholder: 'Vuelvo en 5 minutos', durationLabel: '¿Cuánto tiempo necesitas?', duration60: '1 min', duration300: '5 min', duration600: '10 min', durationCustom: 'Personalizado', secondsLabel: 'segundos', startLabel: '¿Cuándo debe empezar este aviso?', startNow: 'Empezar ahora', scheduleTime: 'Programar una hora', timeLabel: '¿A qué hora local debe empezar?',
12
+ startAction: 'Poner mi aviso en directo', focusAction: 'Mostrar mi escena', exitFocusAction: 'Salir de la escena', resetAction: 'Restablecer mi aviso', flowText: 'Tú eres el streamer: elige qué ocurre, define el tiempo que necesitas y pon tu aviso en directo.', obsTitle: 'Pon esta escena en OBS', obsText: 'Copia el enlace, añade una fuente de navegador en OBS, pégalo y usa la resolución de tu lienzo. STREAMING abre automáticamente la escena limpia a pantalla completa.', obsStepCopy: 'Copiar este enlace', obsStepAdd: 'Añadir una fuente de navegador en OBS', obsStepPaste: 'Pegar y ajustar al tamaño del lienzo', copyUrlAction: 'Copiar enlace de OBS', copiedUrlText: 'Enlace de OBS copiado', streamUrlAria: 'URL de streaming generada para OBS', previewTitle: 'Elige el look de tu escena', previewHint: 'Pulsa una preview para usarla', previewAria: 'Previews de diseños de escena', stageEyebrow: 'Escena de emisión', stageCaption: 'Tu próxima escena está lista', readyBadge: 'Listo', waitingBadge: 'Esperando', liveBadge: 'Aviso en directo', endedBadge: 'Terminado', readyText: 'Revisa la escena y empieza el aviso cuando todo esté listo.', waitingText: 'La cuenta atrás empezará a la hora local programada.', liveText: 'Mantén esta escena visible hasta que el cambio esté preparado.', endedText: 'El aviso ha terminado. Restablécelo o prepara una escena nueva.', remainingLabel: 'Tiempo en escena', startTimeLabel: 'Empieza', endTimeLabel: 'Termina', progressLabel: 'Progreso de la escena', assumptionTitle: 'Nota sobre el tiempo', assumptionText: 'Las horas programadas usan el reloj de tu dispositivo. El contador es una señal visual y no sincroniza OBS, Twitch, el chat ni el codificador.', warningTitle: 'Úsalo como señal de escena', warningText: 'Una pestaña suspendida, un cambio en el reloj del sistema o un retraso de la emisión pueden hacer que el tiempo visible no coincida con el directo. Comprueba la escena antes de cambiar.', invalidTime: 'Escribe una hora local con el formato HH:MM.', clockAria: 'Tiempo restante de la cuenta atrás', statusAria: 'Estado de la cuenta atrás',
13
+ },
14
+ faq: [
15
+ { question: '¿Este contador se conecta a OBS o Twitch?', answer: 'No. Es un temporizador independiente y no controla OBS, Twitch, el chat ni un servidor de streaming. Usa Mostrar mi escena para llenar la pantalla y captura esa vista como fuente de navegador.' },
16
+ { question: '¿Cómo uso el contador directamente en OBS?', answer: 'Añade la URL de la herramienta como fuente de navegador de OBS con el indicador de streaming y sus ajustes, por ejemplo &#63;STREAMING&scene=starting&duration=300&design=aurora&title=Empezamos%20pronto. Los controles desaparecen y el contador llena el espacio de la fuente.' },
17
+ { question: '¿Qué ocurre cuando programo una hora de inicio?', answer: 'El reloj espera hasta la hora local elegida y después cuenta la duración de la escena. Si la hora ya ha pasado, se interpreta como la siguiente ocasión del día siguiente.' },
18
+ { question: '¿Puedo usar un mensaje personalizado?', answer: 'Sí. Como streamer, escribe el aviso breve que quieras enseñar, como la hora de vuelta, el siguiente paso o un mensaje para el raid.' },
19
+ { question: '¿Qué cambian los presets de escena?', answer: 'Nombran el momento de la emisión para que la escena se entienda de un vistazo. No cambian las matemáticas del contador, la duración ni la conexión con ninguna plataforma.' },
20
+ { question: '¿La hora de finalización es exacta?', answer: 'Se calcula con el reloj local del navegador y la duración introducida. El reposo del navegador, una pestaña pausada o un cambio de hora pueden afectar a la actualización visible. Es una señal de planificación, no una garantía de sincronización.' },
21
+ ],
22
+ howTo: [
23
+ { name: 'Elige el momento de la escena', text: 'Selecciona Empezamos pronto, BRB, Raid o Intermedio para nombrar el momento que verán tus espectadores.' },
24
+ { name: 'Escribe tu aviso de streamer', text: 'Introduce el mensaje breve que quieres que lean mientras preparas la siguiente escena.' },
25
+ { name: 'Define la duración o la hora', text: 'Elige una duración habitual o escribe la tuya. Empieza al instante para un aviso en directo o programa una hora local.' },
26
+ { name: 'Lee el estado', text: 'Usa el contador grande y la etiqueta de estado para decidir cuándo cambiar de escena en tu emisión.' },
27
+ ],
28
+ seo: [
29
+ { type: 'title', text: 'Prepara una señal de escena que se entienda al instante', level: 2 },
30
+ { type: 'paragraph', html: 'Una cuenta atrás da a tu escena de empezamos pronto, BRB, raid o intermedio un punto claro de vuelta. Cuenta qué estás haciendo, define el tiempo que necesitas y deja el contador visible mientras preparas la emisión.' },
31
+ { type: 'title', text: 'Qué calcula el contador de escenas', level: 3 },
32
+ { type: 'list', items: ['<strong>Aviso inmediato:</strong> empieza cuando lo pones en directo y descuenta la duración elegida.', '<strong>Aviso programado:</strong> espera a la hora local y después inicia la duración de la escena.', '<strong>Estado de escena:</strong> separa listo, esperando, en directo y terminado para que la siguiente acción quede clara.'] },
33
+ { type: 'title', text: 'Cómo elegir una duración útil', level: 3 },
34
+ { type: 'paragraph', html: 'Usa una duración corta al cambiar fuentes o resolver una interrupción rápida. Elige más tiempo si tienes que preparar a un invitado, un juego o un reinicio técnico. El mensaje debe aportar algo que el contador por sí solo no pueda explicar.' },
35
+ { type: 'tip', title: 'Haz concreta la vuelta', html: 'En vez de prometer algo genérico, escribe la próxima acción: "Vuelvo a las 20:30 con la final" o "Preparando el raid". Así tus espectadores ven cuánto esperan y por qué.' },
36
+ { type: 'title', text: 'Por qué el contador funciona sin conexión', level: 3 },
37
+ { type: 'paragraph', html: 'El contador no necesita acceder a tu canal ni a tu software de emisión. El texto y el tiempo se quedan en el navegador, por lo que sirve como escena de planificación. Antes de emitir, comprueba la visibilidad de la fuente y la transición en OBS.' },
38
+ { type: 'title', text: 'Envía la escena terminada a OBS', level: 3 },
39
+ { type: 'paragraph', html: 'Pulsa <strong>Copiar enlace de OBS</strong>, añade una fuente de navegador a la escena que usas en tu stream, pega el enlace y ajusta la resolución del lienzo. El indicador <code>&#63;STREAMING</code> abre una escena limpia, automática y sin controles.' },
40
+ { type: 'title', text: 'Haz que el diseño encaje con tu canal', level: 3 },
41
+ { type: 'paragraph', html: 'Elige entre cinco composiciones distintas: Bruma aurora conserva el anillo atmosférico, Tipografía cinética construye el contador con números enormes, Pulso expansivo usa ondas que crecen, Señal glitch aporta energía de emisión y Destello solar crea un horizonte cálido. Escoge una preview y ajusta los colores de acento y brillo.' },
42
+ { type: 'list', items: ['<strong>Escena:</strong> etiqueta empezamos pronto, BRB, raid o intermedio.', '<strong>Título:</strong> sustituye la línea predeterminada por tu propio titular.', '<strong>Mensaje:</strong> añade el aviso que tus espectadores necesitan leer.', '<strong>Duración y hora:</strong> controlan el inicio y el tiempo visible de la escena.'] },
43
+ { type: 'paragraph', html: 'Si construyes la URL a mano, puedes empezar con <code>&#63;STREAMING&amp;scene=raid&amp;title=Raid%20en%20marcha&amp;design=pulse</code>. El generador añade automáticamente la duración, el mensaje y tus colores personalizados.' },
44
+ { type: 'title', text: 'Lee la hora final como una referencia', level: 3 },
45
+ { type: 'paragraph', html: 'La hora final sale del reloj de tu dispositivo y de la duración elegida. No garantiza que el directo llegue a todos al mismo instante. Deja un pequeño margen si el cambio depende de otra persona, una conexión invitada o una transición de emisión.' },
46
+ ],
47
+ });
@@ -0,0 +1,47 @@
1
+ import { makeContent } from './locale-factory';
2
+
3
+ export const content = makeContent({
4
+ language: 'fr',
5
+ slug: 'compte-a-rebours-scene-stream-obs',
6
+ title: 'Compte à rebours pour scène de stream',
7
+ description: 'Créez une scène de compte à rebours pensée pour les streamers, pour le démarrage, le BRB, le raid et la pause.',
8
+ ui: {
9
+ sceneLabel: 'Quelle scène préparez-vous ?', sceneBrb: 'BRB', sceneStarting: 'Bientôt en direct', sceneRaid: 'Raid', sceneIntermission: 'Pause',
10
+ sceneTitleLabel: 'Que faut-il afficher au-dessus du compteur ?', sceneTitlePlaceholder: 'Bientôt en direct', designLabel: 'Quelle ambiance doit avoir votre scène ?', designAurora: 'Brume aurora', designType: 'Typographie cinétique', designPulse: 'Pulsation florale', designGlitch: 'Signal glitch', designSunset: 'Éclat solaire',
11
+ accentColorLabel: "Couleur d\'accent", glowColorLabel: 'Couleur de halo', messageLabel: 'Que doivent savoir vos spectateurs ?', messagePlaceholder: 'Je reviens dans 5 minutes', durationLabel: 'De combien de temps avez-vous besoin ?', duration60: '1 min', duration300: '5 min', duration600: '10 min', durationCustom: 'Personnalisé', secondsLabel: 'secondes', startLabel: 'Quand ce signal doit-il commencer ?', startNow: 'Commencer maintenant', scheduleTime: 'Planifier une heure', timeLabel: 'À quelle heure locale doit-il commencer ?',
12
+ startAction: 'Mettre mon signal à l\'antenne', focusAction: 'Afficher ma scène', exitFocusAction: 'Quitter la scène', resetAction: 'Réinitialiser mon signal', flowText: 'Vous êtes le streamer: choisissez le moment, définissez le temps nécessaire, puis mettez votre signal à l\'antenne.', obsTitle: 'Mettre cette scène dans OBS', obsText: 'Copiez le lien, ajoutez une source navigateur dans OBS, collez-le et utilisez la résolution de votre canevas. STREAMING ouvre automatiquement la scène propre en plein écran.', obsStepCopy: 'Copier ce lien', obsStepAdd: 'Ajouter une source navigateur dans OBS', obsStepPaste: 'Coller et adapter à la taille du canevas', copyUrlAction: 'Copier le lien OBS', copiedUrlText: 'Lien OBS copié', streamUrlAria: 'URL de streaming OBS générée', previewTitle: 'Choisir le style de votre scène', previewHint: 'Cliquez sur un aperçu pour le choisir', previewAria: 'Aperçus des styles de scène', stageEyebrow: 'Scène de diffusion', stageCaption: 'Votre prochaine scène est prête', readyBadge: 'Prêt', waitingBadge: 'En attente', liveBadge: 'Signal en direct', endedBadge: 'Terminé', readyText: 'Vérifiez la scène, puis lancez le signal quand elle est prête.', waitingText: 'Le compte à rebours en direct commencera à l\'heure locale prévue.', liveText: 'Gardez cette scène visible jusqu\'à ce que le changement soit prêt.', endedText: 'Le signal est terminé. Réinitialisez-le ou préparez une nouvelle scène.', remainingLabel: 'Temps dans la scène', startTimeLabel: 'Début', endTimeLabel: 'Fin', progressLabel: 'Progression de la scène', assumptionTitle: 'Note sur le temps', assumptionText: 'Les heures planifiées utilisent l\'horloge de votre appareil. Le compteur est un repère visuel et ne synchronise pas OBS, Twitch, le chat ni l\'encodeur.', warningTitle: 'Utilisez-le comme repère de scène', warningText: 'Un onglet en veille, une horloge modifiée ou un retard de diffusion peuvent rendre le temps affiché différent du direct. Vérifiez la scène avant le changement.', invalidTime: 'Saisissez une heure locale au format HH:MM.', clockAria: 'Temps restant du compte à rebours', statusAria: 'État du compte à rebours',
13
+ },
14
+ faq: [
15
+ { question: 'Ce compte à rebours se connecte-t-il à OBS ou Twitch ?', answer: 'Non. Il s\'agit d\'un minuteur autonome qui ne contrôle ni OBS, ni Twitch, ni le chat, ni un serveur de stream. Utilisez Afficher ma scène pour remplir l\'écran, puis capturez cette vue comme source navigateur.' },
16
+ { question: 'Comment utiliser le compte à rebours directement dans OBS ?', answer: 'Ajoutez l\'URL de l\'outil comme source navigateur OBS avec le mode streaming et ses paramètres, par exemple ?STREAMING&scene=starting&duration=300&design=aurora&title=Bientôt%20en%20direct. Les commandes disparaissent et le compte à rebours remplit la zone de la source.' },
17
+ { question: 'Que se passe-t-il quand je planifie une heure de début ?', answer: 'L\'horloge attend l\'heure locale choisie, puis décompte la durée de la scène. Une heure déjà passée est reportée à la prochaine occurrence, le jour suivant.' },
18
+ { question: 'Puis-je utiliser un message personnalisé ?', answer: 'Oui. En tant que streamer, écrivez le signal court que vous voulez montrer, comme une heure de retour, une prochaine étape ou un message de raid.' },
19
+ { question: 'Que changent les préréglages de scène ?', answer: 'Ils nomment le moment de la diffusion afin que la scène soit comprise d\'un coup d\'œil. Le calcul du temps, la durée et la connexion à une plateforme ne changent pas.' },
20
+ { question: 'L\'heure de fin est-elle exacte ?', answer: 'Elle est calculée avec l\'horloge locale du navigateur et la durée saisie. La mise en veille du navigateur, un onglet suspendu ou un changement d\'horloge peuvent modifier l\'actualisation visible. C\'est un repère de préparation, pas une synchronisation de diffusion.' },
21
+ ],
22
+ howTo: [
23
+ { name: 'Choisir le moment de la scène', text: 'Sélectionnez Bientôt en direct, BRB, Raid ou Pause pour nommer ce que vos spectateurs voient.' },
24
+ { name: 'Écrire votre signal de streamer', text: 'Saisissez le court message à lire pendant que vous préparez la scène suivante.' },
25
+ { name: 'Définir la durée ou l\'heure', text: 'Choisissez une durée courante ou saisissez la vôtre. Démarrez immédiatement pour un signal en direct ou planifiez une heure locale.' },
26
+ { name: 'Lire l\'état', text: 'Utilisez la grande horloge et le badge d\'état pour savoir quand changer de scène dans votre diffusion.' },
27
+ ],
28
+ seo: [
29
+ { type: 'title', text: 'Préparer un signal de scène lisible en un instant', level: 2 },
30
+ { type: 'paragraph', html: 'Un compte à rebours donne à votre scène Bientôt en direct, BRB, raid ou pause un point de retour clair. Indiquez ce que vous faites, définissez le temps nécessaire et laissez le grand compteur visible pendant la préparation.' },
31
+ { type: 'title', text: 'Ce que calcule le compteur de scène', level: 3 },
32
+ { type: 'list', items: ['<strong>Signal immédiat :</strong> commence quand vous le mettez à l\'antenne et décompte la durée choisie.', '<strong>Signal planifié :</strong> attend l\'heure locale saisie avant de lancer la durée de la scène.', '<strong>État de la scène :</strong> distingue prêt, attente, direct et terminé pour rendre la prochaine action évidente.'] },
33
+ { type: 'title', text: 'Choisir une durée utile', level: 3 },
34
+ { type: 'paragraph', html: 'Utilisez une durée courte pour changer de source ou revenir d\'une interruption rapide. Prévoyez plus de temps pour préparer un invité, un jeu ou une remise en route technique. Votre message doit apporter une information que l\'horloge seule ne peut pas donner.' },
35
+ { type: 'tip', title: 'Rendre le retour concret', html: 'Au lieu d\'une promesse vague, écrivez l\'action suivante: "De retour à 20 h 30 pour la finale" ou "Raid en préparation". Vos spectateurs comprennent ainsi l\'attente et sa raison.' },
36
+ { type: 'title', text: 'Pourquoi le compteur reste hors ligne', level: 3 },
37
+ { type: 'paragraph', html: 'L\'horloge n\'a pas besoin d\'accéder à votre chaîne ni à votre logiciel de diffusion. Le texte et le temps restent dans le navigateur, ce qui en fait une scène de préparation pratique. Vérifiez tout de même la visibilité de la source et la transition dans OBS avant le direct.' },
38
+ { type: 'title', text: 'Envoyer la scène terminée vers OBS', level: 3 },
39
+ { type: 'paragraph', html: 'Cliquez sur <strong>Copier le lien OBS</strong>, ajoutez une source navigateur à votre scène de stream, collez le lien et adaptez la résolution du canevas. Le mode <code>?STREAMING</code> ouvre une scène propre, autonome et sans commandes.' },
40
+ { type: 'title', text: 'Adapter le style à votre chaîne', level: 3 },
41
+ { type: 'paragraph', html: 'Choisissez entre cinq compositions distinctes: Brume aurora garde un anneau atmosphérique, Typographie cinétique agrandit les chiffres, Pulsation florale déploie des ondes, Signal glitch apporte une énergie de diffusion et Éclat solaire crée un horizon chaud. Choisissez un aperçu et ajustez les couleurs d\'accent et de halo.' },
42
+ { type: 'list', items: ['<strong>Scène :</strong> indique Bientôt en direct, BRB, raid ou pause.', '<strong>Titre :</strong> remplace la ligne par défaut par votre propre accroche.', '<strong>Message :</strong> ajoute le repère que vos spectateurs doivent lire.', '<strong>Durée et heure :</strong> contrôlent le début et le temps d\'affichage.'] },
43
+ { type: 'paragraph', html: 'Si vous construisez l\'URL vous-même, utilisez <code>?STREAMING&amp;scene=raid&amp;title=Raid%20en%20preparation&amp;design=pulse</code>. Le générateur ajoute automatiquement la durée, le message et vos couleurs.' },
44
+ { type: 'title', text: 'Lire l\'heure de fin comme un repère', level: 3 },
45
+ { type: 'paragraph', html: 'L\'heure de fin vient de l\'horloge de votre appareil et de la durée choisie. Elle ne garantit pas que le direct arrive au même instant chez tous les spectateurs. Gardez une marge si le changement dépend d\'un invité, d\'une connexion ou d\'une transition de diffusion.' },
46
+ ],
47
+ });