Merge branch 'refactor/task-10b-bilingual-blocks'
This commit is contained in:
@@ -9,22 +9,24 @@
|
|||||||
// hairlines between worker cards; the parent background is an off-token seam
|
// hairlines between worker cards; the parent background is an off-token seam
|
||||||
// colour marked inline with `token-gap`.
|
// colour marked inline with `token-gap`.
|
||||||
|
|
||||||
|
type Localized = { en: string; pt: string };
|
||||||
|
|
||||||
interface Worker {
|
interface Worker {
|
||||||
/** Used as the `data-worker` hook and the key in the source. */
|
/** Used as the `data-worker` hook and the key in the source. */
|
||||||
id: string;
|
id: string;
|
||||||
/** Short label rendered uppercase, e.g. "UI". */
|
/** Short label rendered uppercase, e.g. "UI". */
|
||||||
label: string;
|
label: string | Localized;
|
||||||
/** Body copy describing the worker's remit. */
|
/** Body copy describing the worker's remit. */
|
||||||
strong: string;
|
strong: string | Localized;
|
||||||
/** Path label, e.g. "agent/ui". */
|
/** Path label, e.g. "agent/ui". */
|
||||||
code: string;
|
code: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
orchestrator: {
|
orchestrator: {
|
||||||
eyebrow: string;
|
eyebrow: string | Localized;
|
||||||
/** May contain inline `<br>`; rendered with `set:html`. */
|
/** May contain inline `<br>`; rendered with `set:html`. */
|
||||||
title: string;
|
title: string | Localized;
|
||||||
code: string;
|
code: string;
|
||||||
};
|
};
|
||||||
workers: Worker[];
|
workers: Worker[];
|
||||||
@@ -36,8 +38,32 @@ const { orchestrator, workers, initial = workers[0]?.id } = Astro.props;
|
|||||||
|
|
||||||
<div class="fleet-grid">
|
<div class="fleet-grid">
|
||||||
<article class="captain">
|
<article class="captain">
|
||||||
<span class="captain-eyebrow">{orchestrator.eyebrow}</span>
|
<span class="captain-eyebrow">
|
||||||
<h2 set:html={orchestrator.title} />
|
{
|
||||||
|
typeof orchestrator.eyebrow === 'string' ? (
|
||||||
|
orchestrator.eyebrow
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{orchestrator.eyebrow.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{orchestrator.eyebrow.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
{
|
||||||
|
typeof orchestrator.title === 'string' ? (
|
||||||
|
<h2 set:html={orchestrator.title} />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<h2 data-language-content="en" set:html={orchestrator.title.en} />
|
||||||
|
<h2 data-language-content="pt" hidden set:html={orchestrator.title.pt} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
<code>{orchestrator.code}</code>
|
<code>{orchestrator.code}</code>
|
||||||
</article>
|
</article>
|
||||||
<div class="arrow" aria-hidden="true">→</div>
|
<div class="arrow" aria-hidden="true">→</div>
|
||||||
@@ -49,8 +75,30 @@ const { orchestrator, workers, initial = workers[0]?.id } = Astro.props;
|
|||||||
data-worker={worker.id}
|
data-worker={worker.id}
|
||||||
aria-pressed={worker.id === initial}
|
aria-pressed={worker.id === initial}
|
||||||
>
|
>
|
||||||
<span>{worker.label}</span>
|
<span>
|
||||||
<strong>{worker.strong}</strong>
|
{typeof worker.label === 'string' ? (
|
||||||
|
worker.label
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{worker.label.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{worker.label.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<strong>
|
||||||
|
{typeof worker.strong === 'string' ? (
|
||||||
|
worker.strong
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{worker.strong.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{worker.strong.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</strong>
|
||||||
<code>{worker.code}</code>
|
<code>{worker.code}</code>
|
||||||
</button>
|
</button>
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -7,18 +7,20 @@
|
|||||||
// muted body) and lives here so the next page that needs it gets the same
|
// muted body) and lives here so the next page that needs it gets the same
|
||||||
// beat for free.
|
// beat for free.
|
||||||
|
|
||||||
|
type Localized = { en: string; pt: string };
|
||||||
|
|
||||||
interface Row {
|
interface Row {
|
||||||
/** The package name, rendered as a `<th>` (column 1). */
|
/** The package name, rendered as a `<th>` (column 1). */
|
||||||
package: string;
|
package: string | Localized;
|
||||||
/** What the package contains (column 2). */
|
/** What the package contains (column 2). */
|
||||||
contains: string;
|
contains: string | Localized;
|
||||||
/** Why this matters (column 3). */
|
/** Why this matters (column 3). */
|
||||||
why: string;
|
why: string | Localized;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/** Column headers in render order. */
|
/** Column headers in render order. */
|
||||||
columns: [string, string, string];
|
columns: [string | Localized, string | Localized, string | Localized];
|
||||||
rows: Row[];
|
rows: Row[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,18 +30,96 @@ const { columns, rows } = Astro.props;
|
|||||||
<table class="handoff-table">
|
<table class="handoff-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{columns[0]}</th>
|
<th>
|
||||||
<th>{columns[1]}</th>
|
{
|
||||||
<th>{columns[2]}</th>
|
typeof columns[0] === 'string' ? (
|
||||||
|
columns[0]
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{columns[0].en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{columns[0].pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{
|
||||||
|
typeof columns[1] === 'string' ? (
|
||||||
|
columns[1]
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{columns[1].en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{columns[1].pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{
|
||||||
|
typeof columns[2] === 'string' ? (
|
||||||
|
columns[2]
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{columns[2].en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{columns[2].pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{
|
{
|
||||||
rows.map((row) => (
|
rows.map((row) => (
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{row.package}</th>
|
<th scope="row">
|
||||||
<td>{row.contains}</td>
|
{typeof row.package === 'string' ? (
|
||||||
<td>{row.why}</td>
|
row.package
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{row.package.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{row.package.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
{typeof row.contains === 'string' ? (
|
||||||
|
row.contains
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{row.contains.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{row.contains.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{typeof row.why === 'string' ? (
|
||||||
|
row.why
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{row.why.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{row.why.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,18 +8,20 @@
|
|||||||
//
|
//
|
||||||
// Every `data-phase` value is asserted by `scripts/verify.mjs`.
|
// Every `data-phase` value is asserted by `scripts/verify.mjs`.
|
||||||
|
|
||||||
|
type Localized = { en: string; pt: string };
|
||||||
|
|
||||||
export type PhaseId = 'plan' | 'build' | 'review';
|
export type PhaseId = 'plan' | 'build' | 'review';
|
||||||
|
|
||||||
interface Phase {
|
interface Phase {
|
||||||
id: PhaseId;
|
id: PhaseId;
|
||||||
/** Two-letter label rendered in the tab, e.g. "PLAN". */
|
/** Two-letter label rendered in the tab, e.g. "PLAN". */
|
||||||
label: string;
|
label: string | Localized;
|
||||||
/** Numeric prefix, e.g. "01". */
|
/** Numeric prefix, e.g. "01". */
|
||||||
number: string;
|
number: string;
|
||||||
title: string;
|
title: string | Localized;
|
||||||
body: string;
|
body: string | Localized;
|
||||||
/** Headline + small caption shown above the panel title. */
|
/** Headline + small caption shown above the panel title. */
|
||||||
meta: { deliverable: string; gate: string };
|
meta: { deliverable: string | Localized; gate: string | Localized };
|
||||||
/** Code-line evidence shown at the bottom of the panel. */
|
/** Code-line evidence shown at the bottom of the panel. */
|
||||||
evidence: string;
|
evidence: string;
|
||||||
}
|
}
|
||||||
@@ -43,7 +45,17 @@ const active = phases.find((phase) => phase.id === initial) ?? phases[0];
|
|||||||
role="tab"
|
role="tab"
|
||||||
aria-selected={phase.id === initial}
|
aria-selected={phase.id === initial}
|
||||||
>
|
>
|
||||||
<b>{phase.number}</b> {phase.label}
|
<b>{phase.number}</b>{' '}
|
||||||
|
{typeof phase.label === 'string' ? (
|
||||||
|
phase.label
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{phase.label.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{phase.label.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -51,11 +63,71 @@ const active = phases.find((phase) => phase.id === initial) ?? phases[0];
|
|||||||
|
|
||||||
<article class="phase-panel" id="phase-panel" aria-live="polite">
|
<article class="phase-panel" id="phase-panel" aria-live="polite">
|
||||||
<div class="phase-meta">
|
<div class="phase-meta">
|
||||||
<span>{active.meta.deliverable}</span>
|
<span>
|
||||||
<small>{active.meta.gate}</small>
|
{
|
||||||
|
typeof active.meta.deliverable === 'string' ? (
|
||||||
|
active.meta.deliverable
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{active.meta.deliverable.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{active.meta.deliverable.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
<small>
|
||||||
|
{
|
||||||
|
typeof active.meta.gate === 'string' ? (
|
||||||
|
active.meta.gate
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{active.meta.gate.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{active.meta.gate.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<h3>{active.title}</h3>
|
<h3>
|
||||||
<p>{active.body}</p>
|
{
|
||||||
|
typeof active.title === 'string' ? (
|
||||||
|
active.title
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{active.title.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{active.title.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
{
|
||||||
|
typeof active.body === 'string' ? (
|
||||||
|
active.body
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{active.body.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{active.body.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</p>
|
||||||
<code>{active.evidence}</code>
|
<code>{active.evidence}</code>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,16 @@
|
|||||||
// renders the column header + the four rows; task 15 will hydrate the
|
// renders the column header + the four rows; task 15 will hydrate the
|
||||||
// "route detail" panel to the right.
|
// "route detail" panel to the right.
|
||||||
|
|
||||||
|
type Localized = { en: string; pt: string };
|
||||||
|
|
||||||
interface Route {
|
interface Route {
|
||||||
id: 'plan' | 'build' | 'explore' | 'review' | string;
|
id: 'plan' | 'build' | 'explore' | 'review' | string;
|
||||||
/** Strong label, e.g. "Plan". */
|
/** Strong label, e.g. "Plan". */
|
||||||
strong: string;
|
strong: string | Localized;
|
||||||
/** Profile descriptor, e.g. "strong / broad". */
|
/** Profile descriptor, e.g. "strong / broad". */
|
||||||
profile: string;
|
profile: string | Localized;
|
||||||
/** Prompt shape copy. */
|
/** Prompt shape copy. */
|
||||||
prompt: string;
|
prompt: string | Localized;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -37,9 +39,42 @@ const { routes, initial = routes[0]?.id ?? 'plan' } = Astro.props;
|
|||||||
data-route={route.id}
|
data-route={route.id}
|
||||||
aria-pressed={route.id === initial}
|
aria-pressed={route.id === initial}
|
||||||
>
|
>
|
||||||
<strong>{route.strong}</strong>
|
<strong>
|
||||||
<b>{route.profile}</b>
|
{typeof route.strong === 'string' ? (
|
||||||
<small>{route.prompt}</small>
|
route.strong
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{route.strong.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{route.strong.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</strong>
|
||||||
|
<b>
|
||||||
|
{typeof route.profile === 'string' ? (
|
||||||
|
route.profile
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{route.profile.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{route.profile.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</b>
|
||||||
|
<small>
|
||||||
|
{typeof route.prompt === 'string' ? (
|
||||||
|
route.prompt
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{route.prompt.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{route.prompt.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</small>
|
||||||
</button>
|
</button>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,14 @@
|
|||||||
// paired with a `<article>` detail panel by the parent page; this component
|
// paired with a `<article>` detail panel by the parent page; this component
|
||||||
// renders only the picker.
|
// renders only the picker.
|
||||||
|
|
||||||
|
type Localized = { en: string; pt: string };
|
||||||
|
|
||||||
interface PackageFile {
|
interface PackageFile {
|
||||||
id: 'skill' | 'references' | 'scripts' | 'assets' | string;
|
id: 'skill' | 'references' | 'scripts' | 'assets' | string;
|
||||||
/** Path rendered inside `<code>`, e.g. "SKILL.md". */
|
/** Path rendered inside `<code>`, e.g. "SKILL.md". */
|
||||||
path: string;
|
path: string;
|
||||||
/** Helper copy under the path. */
|
/** Helper copy under the path. */
|
||||||
small: string;
|
small: string | Localized;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -34,7 +36,18 @@ const { files, initial = files[0]?.id ?? 'skill' } = Astro.props;
|
|||||||
aria-selected={file.id === initial}
|
aria-selected={file.id === initial}
|
||||||
>
|
>
|
||||||
<code>{file.path}</code>
|
<code>{file.path}</code>
|
||||||
<small>{file.small}</small>
|
<small>
|
||||||
|
{typeof file.small === 'string' ? (
|
||||||
|
file.small
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{file.small.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{file.small.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</small>
|
||||||
</button>
|
</button>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,17 @@
|
|||||||
// the `data-tree` hook asserted by `scripts/verify.mjs`. The `root` node and
|
// the `data-tree` hook asserted by `scripts/verify.mjs`. The `root` node and
|
||||||
// the `initial` branch are marked selected.
|
// the `initial` branch are marked selected.
|
||||||
|
|
||||||
|
type Localized = { en: string; pt: string };
|
||||||
|
|
||||||
interface Branch {
|
interface Branch {
|
||||||
/** The `data-tree` hook, e.g. "ui", "tests", "docs". */
|
/** The `data-tree` hook, e.g. "ui", "tests", "docs". */
|
||||||
id: string;
|
id: string;
|
||||||
/** Uppercase label, e.g. "UI AGENT". */
|
/** Uppercase label, e.g. "UI AGENT". */
|
||||||
label: string;
|
label: string | Localized;
|
||||||
/** Strong line, e.g. "agent/ui". */
|
/** Strong line, e.g. "agent/ui". */
|
||||||
strong: string;
|
strong: string;
|
||||||
/** Status small, e.g. "3 files · working". */
|
/** Status small, e.g. "3 files · working". */
|
||||||
small: string;
|
small: string | Localized;
|
||||||
/** CSS modifier so each branch picks up its tone. */
|
/** CSS modifier so each branch picks up its tone. */
|
||||||
tone: 'ui' | 'tests' | 'docs' | string;
|
tone: 'ui' | 'tests' | 'docs' | string;
|
||||||
}
|
}
|
||||||
@@ -50,9 +52,31 @@ const { branches, initial = 'main' } = Astro.props;
|
|||||||
aria-selected={branch.id === initial}
|
aria-selected={branch.id === initial}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
<span>{branch.label}</span>
|
<span>
|
||||||
|
{typeof branch.label === 'string' ? (
|
||||||
|
branch.label
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{branch.label.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{branch.label.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
<strong>{branch.strong}</strong>
|
<strong>{branch.strong}</strong>
|
||||||
<small>{branch.small}</small>
|
<small>
|
||||||
|
{typeof branch.small === 'string' ? (
|
||||||
|
branch.small
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span data-language-content="en">{branch.small.en}</span>
|
||||||
|
<span data-language-content="pt" hidden>
|
||||||
|
{branch.small.pt}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</small>
|
||||||
</>
|
</>
|
||||||
</button>
|
</button>
|
||||||
))
|
))
|
||||||
|
|||||||
Reference in New Issue
Block a user