feat: add model routing and hands-on lab
This commit is contained in:
@@ -4,6 +4,159 @@ const phases = {
|
||||
review: { model: { en: 'STRONG MODEL OR HUMAN', pt: 'MODELO FORTE OU HUMANO' }, title: { en: 'Reconnect result to intent', pt: 'Reconecte o resultado à intenção' }, copy: { en: 'Check the diff against the original brief, run the checks, then merge, request changes, or discard.', pt: 'Compare o diff com o brief original, execute as verificações e então faça merge, peça mudanças ou descarte.' }, code: { en: 'diff + checks → review → merge / iterate', pt: 'diff + verificações → revisar → merge / iterar' } }
|
||||
};
|
||||
|
||||
const handsOnPrompts = {
|
||||
en: {
|
||||
basic: [
|
||||
'Work only in hands-on/starter. It is dependency-free HTML, CSS, and JavaScript.',
|
||||
'',
|
||||
'Add an All / Open / Done filter to Tiny Tasks.',
|
||||
'',
|
||||
'Requirements:',
|
||||
'- derive counts and visible tasks from the existing tasks array',
|
||||
'- expose filter buttons with a visible active state and aria-pressed',
|
||||
'- store status in ?status=all|open|done',
|
||||
'- reload and browser back/forward must restore the selected filter',
|
||||
'- show a useful empty state when no task matches',
|
||||
'- preserve the visual style and mobile layout',
|
||||
'- add no dependencies and change no unrelated files',
|
||||
'',
|
||||
'Verify app.js syntax and exercise every filter plus URL navigation.',
|
||||
'Return changed files, checks run, results, and remaining risk.'
|
||||
].join('\n'),
|
||||
skills: [
|
||||
'Use $ponytail-lite and $webapp-testing.',
|
||||
'Work only in hands-on/starter. It is dependency-free HTML, CSS, and JavaScript.',
|
||||
'',
|
||||
'Add an All / Open / Done filter to Tiny Tasks.',
|
||||
'',
|
||||
'Apply $ponytail-lite: inspect first, reuse the current render flow, prefer native URL and button APIs, and avoid dependencies or abstractions.',
|
||||
'Apply $webapp-testing: verify all filters, aria-pressed, reload, browser back/forward, empty state, and one mobile viewport.',
|
||||
'',
|
||||
'Acceptance:',
|
||||
'- counts and visible tasks come from the existing tasks array',
|
||||
'- ?status=all|open|done is the source of truth',
|
||||
'- invalid status falls back safely to all',
|
||||
'- style remains consistent; unrelated files remain untouched',
|
||||
'',
|
||||
'Return the smallest working diff and concrete verification evidence.'
|
||||
].join('\n')
|
||||
},
|
||||
pt: {
|
||||
basic: [
|
||||
'Trabalhe apenas em hands-on/starter. É HTML, CSS e JavaScript sem dependências.',
|
||||
'',
|
||||
'Adicione um filtro Todos / Abertos / Concluídos ao Tiny Tasks.',
|
||||
'',
|
||||
'Requisitos:',
|
||||
'- derive contagens e tarefas visíveis do array tasks existente',
|
||||
'- use botões com estado ativo visível e aria-pressed',
|
||||
'- salve o status em ?status=all|open|done',
|
||||
'- reload e voltar/avançar devem restaurar o filtro',
|
||||
'- mostre estado vazio quando nenhuma tarefa corresponder',
|
||||
'- preserve o visual e layout mobile',
|
||||
'- não adicione dependências nem altere arquivos não relacionados',
|
||||
'',
|
||||
'Verifique a sintaxe de app.js e teste filtros e navegação por URL.',
|
||||
'Retorne arquivos alterados, checks, resultados e risco restante.'
|
||||
].join('\n'),
|
||||
skills: [
|
||||
'Use $ponytail-lite e $webapp-testing.',
|
||||
'Trabalhe apenas em hands-on/starter. É HTML, CSS e JavaScript sem dependências.',
|
||||
'',
|
||||
'Adicione um filtro Todos / Abertos / Concluídos ao Tiny Tasks.',
|
||||
'',
|
||||
'Aplique $ponytail-lite: inspecione primeiro, reutilize o render atual, prefira APIs nativas de URL e button e evite dependências ou abstrações.',
|
||||
'Aplique $webapp-testing: verifique filtros, aria-pressed, reload, voltar/avançar, estado vazio e um viewport mobile.',
|
||||
'',
|
||||
'Aceitação:',
|
||||
'- contagens e tarefas visíveis vêm do array tasks existente',
|
||||
'- ?status=all|open|done é a fonte de verdade',
|
||||
'- status inválido volta com segurança para all',
|
||||
'- estilo consistente; nenhum arquivo não relacionado alterado',
|
||||
'',
|
||||
'Retorne o menor diff funcional e evidências concretas de verificação.'
|
||||
].join('\n')
|
||||
}
|
||||
};
|
||||
|
||||
const modelGuide = {
|
||||
providers: {
|
||||
openai: {
|
||||
label: 'OpenAI', source: 'https://developers.openai.com/api/docs/guides/latest-model',
|
||||
title: { en: 'Sol · Terra · Luna', pt: 'Sol · Terra · Luna' },
|
||||
copy: { en: 'GPT-5.6 separates capability tier from reasoning effort. Sol is flagship, Terra balances performance and cost, and Luna targets efficient high-volume work.', pt: 'O GPT-5.6 separa o nível de capacidade do esforço de raciocínio. Sol é flagship, Terra equilibra desempenho e custo, e Luna atende trabalho eficiente em alto volume.' },
|
||||
tiers: [
|
||||
['STRONG', 'Sol', { en: 'orchestration + hard judgment', pt: 'orquestração + julgamento difícil' }],
|
||||
['BALANCED', 'Terra', { en: 'normal implementation', pt: 'implementação normal' }],
|
||||
['FAST', 'Luna', { en: 'bounded, high-volume work', pt: 'trabalho delimitado e volumoso' }]
|
||||
],
|
||||
config: 'reasoning: { effort: "medium" }'
|
||||
},
|
||||
claude: {
|
||||
label: 'Claude', source: 'https://docs.anthropic.com/en/docs/claude-code/model-config',
|
||||
title: { en: 'Opus · Sonnet · Haiku', pt: 'Opus · Sonnet · Haiku' },
|
||||
copy: { en: 'Claude Code exposes memorable aliases. Opus handles complex reasoning, Sonnet everyday coding, and Haiku simple fast work. The opusplan alias can plan with Opus and execute with Sonnet.', pt: 'Claude Code oferece aliases fáceis de lembrar. Opus cuida de raciocínio complexo, Sonnet do código cotidiano e Haiku de trabalho simples e rápido. O alias opusplan pode planejar com Opus e executar com Sonnet.' },
|
||||
tiers: [
|
||||
['STRONG', 'Opus', { en: 'planning + architecture', pt: 'planejamento + arquitetura' }],
|
||||
['BALANCED', 'Sonnet', { en: 'everyday coding', pt: 'código cotidiano' }],
|
||||
['FAST', 'Haiku', { en: 'simple, fast tasks', pt: 'tarefas simples e rápidas' }]
|
||||
],
|
||||
config: '/model opus · /model sonnet · /model haiku'
|
||||
},
|
||||
gemini: {
|
||||
label: 'Gemini', source: 'https://ai.google.dev/gemini-api/docs/thinking',
|
||||
title: { en: 'Pro · Flash · Flash-Lite', pt: 'Pro · Flash · Flash-Lite' },
|
||||
copy: { en: 'Gemini uses model families rather than interchangeable aliases. Pro targets complex reasoning, Flash balances capability and throughput, and Flash-Lite prioritizes latency and cost.', pt: 'Gemini usa famílias de modelos, não aliases intercambiáveis. Pro mira raciocínio complexo, Flash equilibra capacidade e throughput, e Flash-Lite prioriza latência e custo.' },
|
||||
tiers: [
|
||||
['STRONG', 'Pro', { en: 'complex reasoning', pt: 'raciocínio complexo' }],
|
||||
['BALANCED', 'Flash', { en: 'capability + throughput', pt: 'capacidade + throughput' }],
|
||||
['FAST', 'Flash-Lite', { en: 'latency + cost', pt: 'latência + custo' }]
|
||||
],
|
||||
config: 'thinkingConfig: { thinkingLevel: "MEDIUM" }'
|
||||
}
|
||||
},
|
||||
efforts: {
|
||||
low: { en: ['LOW', 'Use for formatting, lookup, narrow edits, and well-specified worker tasks. Optimize for fast feedback.', 'bounded task → low'], pt: ['BAIXO', 'Use para formatação, consulta, edições estreitas e tarefas de worker bem especificadas. Otimize para feedback rápido.', 'tarefa delimitada → baixo'] },
|
||||
medium: { en: ['MEDIUM', 'Balanced starting point for normal implementation, tests, and review. Measure before moving up.', 'normal build → medium'], pt: ['MÉDIO', 'Ponto inicial equilibrado para implementação normal, testes e revisão. Meça antes de subir.', 'build normal → médio'] },
|
||||
high: { en: ['HIGH', 'Use for architecture, orchestration, hard debugging, and consequential review where added latency is justified.', 'ambiguity + risk → high'], pt: ['ALTO', 'Use para arquitetura, orquestração, diagnóstico difícil e revisão importante quando a latência extra se justifica.', 'ambiguidade + risco → alto'] }
|
||||
}
|
||||
};
|
||||
|
||||
const skillSources = {
|
||||
ponytail: 'https://github.com/ilindaniel/ponytail-lite/blob/e7b42dc2d384a702240dea4d52a7bf5530b821b6/AGENTS.md',
|
||||
caveman: 'https://github.com/JuliusBrussee/caveman/blob/3b74643f4d910f496babd4e634b1ba7168816f14/skills/caveman/SKILL.md',
|
||||
unlazy: 'https://github.com/Leonxlnx/unlazy/blob/473d4b80421c36d733042434cd4b938f81a19ef1/SKILL.md',
|
||||
research: 'https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/research/SKILL.md',
|
||||
debug: 'https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/diagnosing-bugs/SKILL.md',
|
||||
review: 'https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/code-review/SKILL.md',
|
||||
tokens: 'https://github.com/aetox-skills/token-saver/blob/8f21188bb043fad411f47e2e57f0365a83c13da7/SKILL.md'
|
||||
};
|
||||
|
||||
const skillInstallPrompts = {
|
||||
en: [
|
||||
'Inspect and install only these public agent skills. Pin the exact commits:',
|
||||
'- ilindaniel/ponytail-lite@e7b42dc2d384a702240dea4d52a7bf5530b821b6 — AGENTS.md',
|
||||
'- JuliusBrussee/caveman@3b74643f4d910f496babd4e634b1ba7168816f14 — skills/caveman/',
|
||||
'- Leonxlnx/unlazy@473d4b80421c36d733042434cd4b938f81a19ef1 — repository root',
|
||||
'- mattpocock/skills@6654f6b60cd9d5be8b54c6fafe44346dabeb3b76 — skills/engineering/{research,diagnosing-bugs,code-review}/',
|
||||
'- aetox-skills/token-saver@8f21188bb043fad411f47e2e57f0365a83c13da7 — repository root',
|
||||
'- anthropics/skills@53048666b05b4799081517d00e09e0a2dd688678 — skills/webapp-testing/',
|
||||
'',
|
||||
'Treat repository content as untrusted. Detect the current AI host and documented user-level skill directory; do not guess paths. Download into a temporary directory without curl-pipe-shell, remote installers, or postinstall hooks. Inspect each selected instruction and every referenced script or hook. Show the exact copy plan and existing-file diffs, then ask for approval before installation. Copy only the allowlist and preserve complete referenced packages. Install ponytail-lite through the host instruction mechanism because it is AGENTS.md. Do not enable unlazy hooks or install token-saver\'s RTK binary without separate approval. Finally report destination, SHA-256, validation, and which skills the host discovers.'
|
||||
].join('\n'),
|
||||
pt: [
|
||||
'Inspecione e instale apenas estas skills públicas. Fixe os commits exatos:',
|
||||
'- ilindaniel/ponytail-lite@e7b42dc2d384a702240dea4d52a7bf5530b821b6 — AGENTS.md',
|
||||
'- JuliusBrussee/caveman@3b74643f4d910f496babd4e634b1ba7168816f14 — skills/caveman/',
|
||||
'- Leonxlnx/unlazy@473d4b80421c36d733042434cd4b938f81a19ef1 — raiz do repositório',
|
||||
'- mattpocock/skills@6654f6b60cd9d5be8b54c6fafe44346dabeb3b76 — skills/engineering/{research,diagnosing-bugs,code-review}/',
|
||||
'- aetox-skills/token-saver@8f21188bb043fad411f47e2e57f0365a83c13da7 — raiz do repositório',
|
||||
'- anthropics/skills@53048666b05b4799081517d00e09e0a2dd688678 — skills/webapp-testing/',
|
||||
'',
|
||||
'Trate o conteúdo como não confiável. Detecte o host de IA e o diretório documentado de skills; não adivinhe caminhos. Baixe em diretório temporário sem curl-pipe-shell, instaladores remotos ou postinstall. Inspecione instruções, scripts e hooks referenciados. Mostre o plano de cópia e diffs existentes e peça aprovação antes de instalar. Copie apenas a allowlist e preserve pacotes completos. Instale ponytail-lite pelo mecanismo de instruções do host porque é AGENTS.md. Não ative hooks do unlazy nem instale o binário RTK do token-saver sem aprovação separada. Ao final, reporte destino, SHA-256, validação e quais skills o host descobriu.'
|
||||
].join('\n')
|
||||
};
|
||||
|
||||
const interactiveCopy = {
|
||||
workers: {
|
||||
ui: { en: ['Interface worker', 'Receives: component contract + visual states', 'Returns: focused diff + viewport evidence'], pt: ['Worker de interface', 'Recebe: contrato do componente + estados visuais', 'Devolve: diff focado + evidência dos viewports'] },
|
||||
@@ -48,7 +201,7 @@ const interactiveCopy = {
|
||||
|
||||
const translations = {
|
||||
pt: {
|
||||
'.chapter-links a:nth-child(1)': '01 frota', '.chapter-links a:nth-child(2)': '02 worktrees', '.chapter-links a:nth-child(3)': '03 skills', '.chapter-links a:nth-child(4)': '04 criar', '.chapter-links a:nth-child(5)': '05 kit de campo', '.edition': 'ENGENHARIA DE IA <i></i> 01 / 2026',
|
||||
'.chapter-links a:nth-child(1)': '01 frota', '.chapter-links a:nth-child(2)': '02 worktrees', '.chapter-links a:nth-child(3)': '03 modelos', '.chapter-links a:nth-child(4)': '04 skills', '.chapter-links a:nth-child(5)': '05 criar', '.chapter-links a:nth-child(6)': '06 kit de campo', '.chapter-links a:nth-child(7)': '07 prática', '.edition': 'ENGENHARIA DE IA <i></i> 01 / 2026',
|
||||
'.hero .eyebrow': 'Uma apresentação para quem entrega software', '.lede': 'Você não precisa de um exército de modelos. Precisa de um sistema: uma mente para enquadrar o trabalho, várias mãos para executá-lo e uma fronteira clara entre cada tarefa.', '.hero-index span': 'NOTA DE CAMPO / 001', '.hero-index strong': 'Entregue o<br /><em>sistema.</em>', '.hero-index small': 'Skills · agentes · worktrees · evidências',
|
||||
'.hero-stats div:nth-child(1) span': 'modelo forte<br />para ambiguidade', '.hero-stats div:nth-child(2) span': 'workers delimitados<br />em paralelo', '.hero-stats div:nth-child(3) span': 'iterações<br />com evidências', '.hero-stats p': 'Leia isto como um mapa de rota, não como uma receita de prompt.',
|
||||
'.thesis span': 'REGRA ZERO', '.thesis strong': 'Modelo forte para ambiguidade.<br />Modelo leve para trabalho delimitado.', '.fleet .section-label span:nth-child(1)': 'Uma pequena frota', '.fleet .section-label span:nth-child(2)': 'coordenação antes do paralelismo', '.captain span': 'ORQUESTRADOR', '.captain h2': 'Decide o que<br />precisa acontecer.', '.worker-card[data-worker="ui"] strong': 'Componentes e estados visuais', '.worker-card[data-worker="tests"] strong': 'Casos de aceitação', '.worker-card[data-worker="docs"] strong': 'Guia e exemplos', '.caption': 'O orquestrador preserva a intenção, escreve pequenos contratos e reúne resultados verificáveis. Ele não precisa digitar cada linha.',
|
||||
@@ -60,6 +213,11 @@ const translations = {
|
||||
};
|
||||
|
||||
Object.assign(translations.pt, {
|
||||
'.model-gearbox .section-label span:nth-child(1)': 'Câmbio de modelos', '.model-gearbox .section-label span:nth-child(2)': 'nível de capacidade × esforço de raciocínio',
|
||||
'.gearbox-intro .eyebrow': 'Dois controles separados', '.gearbox-intro h2': 'Escolha o motor.<br />Depois escolha a <em>marcha.</em>',
|
||||
'.gearbox-intro > p': 'Um modelo mais forte muda o teto de capacidade. Mais esforço de raciocínio dá mais espaço para esse modelo trabalhar. Comece com a combinação mais leve que passa seus checks e mova um controle por vez.',
|
||||
'.effort-rail > span': 'RACIOCÍNIO / PENSAMENTO', '[data-effort="low"] b': 'BAIXO', '[data-effort="low"] small': 'delimitado + rápido', '[data-effort="medium"] b': 'MÉDIO', '[data-effort="medium"] small': 'ponto inicial', '[data-effort="high"] b': 'ALTO', '[data-effort="high"] small': 'complexo + custoso',
|
||||
'.gearbox-rule span': 'REGRA DE ROTEAMENTO', '.gearbox-rule strong': 'Use modelos fortes para ambiguidade e julgamento. Use modelos leves para execução delimitada. Aumente o esforço apenas quando a avaliação mostrar ganho.',
|
||||
'.skill-builder .section-label span:nth-child(1)': 'Criar uma skill',
|
||||
'.skill-builder .section-label span:nth-child(2)': 'atrito repetido → julgamento reutilizável',
|
||||
'.builder-intro .eyebrow': 'A forja de skills',
|
||||
@@ -72,7 +230,18 @@ Object.assign(translations.pt, {
|
||||
'[data-skill-step="validate"] span': 'Validar', '[data-skill-step="validate"] small': 'teste comportamento real',
|
||||
'.artifact-head span': 'SAÍDA / PACOTE DE SKILL', '.artifact-command span': 'VALIDAR',
|
||||
'.builder-loop > span': 'APÓS USO REAL',
|
||||
'.builder-loop > div': '<b>observar falha</b><i>→</i><b>refinar uma regra</b><i>→</i><b>retestar comportamento</b><i>→</i><b>manter estreita</b>'
|
||||
'.builder-loop > div': '<b>observar falha</b><i>→</i><b>refinar uma regra</b><i>→</i><b>retestar comportamento</b><i>→</i><b>manter estreita</b>',
|
||||
'.install-skills header span': 'PACOTE DE INSTALAÇÃO', '.install-skills header strong': 'Peça ao seu agente para verificar, instalar e validar as skills.',
|
||||
'.install-skills footer': 'Revise cada fonte antes da instalação. Skills locais existentes devem ser preservadas.',
|
||||
'.hands-on .section-label span:nth-child(1)': 'Prática', '.hands-on .section-label span:nth-child(2)': '10 minutos / uma feature ausente',
|
||||
'.hands-intro .eyebrow': 'Laboratório Tiny Tasks', '.hands-intro h2': 'Mesma tarefa.<br />Melhor <em>sistema operacional.</em>',
|
||||
'.hands-intro > div:last-child > p': 'Comece com um quadro estático propositalmente incompleto. Execute um prompt, restaure e execute a versão com skills. Compare tamanho do diff, evidências e complexidade desnecessária.',
|
||||
'.starter-link': 'Abrir o projeto inicial →', '.exercise-brief > span': 'A FEATURE AUSENTE',
|
||||
'.exercise-brief > strong': 'Adicione filtros Todos / Abertos / Concluídos que sobrevivem reload e navegação.',
|
||||
'.exercise-brief > div': '<b>STACK</b> HTML · CSS · JavaScript <b>DEPENDÊNCIAS</b> nenhuma <b>ARQUIVOS</b> 3',
|
||||
'.prompt-card:first-child header strong': 'Bom prompt', '.prompt-card.enhanced header strong': 'Bom prompt + skills',
|
||||
'.prompt-card:first-child footer': 'Contexto claro · restrições · aceitação · evidência', '.prompt-card.enhanced footer': 'Mesmo contrato · métodos explícitos · prova mais forte',
|
||||
'.comparison-strip > span': 'COMPARE AS EXECUÇÕES', '.comparison-strip > div:nth-child(2)': '<b>01</b> Arquivos alterados', '.comparison-strip > div:nth-child(3)': '<b>02</b> Novas dependências', '.comparison-strip > div:nth-child(4)': '<b>03</b> Checks executados', '.comparison-strip > div:nth-child(5)': '<b>04</b> Evidências retornadas'
|
||||
});
|
||||
|
||||
const panel = document.querySelector('#phase-panel');
|
||||
@@ -121,6 +290,23 @@ function renderRoute(id) {
|
||||
selectButtons('[data-route]', id, 'route');
|
||||
}
|
||||
|
||||
function renderModelProvider(id) {
|
||||
const item = modelGuide.providers[id];
|
||||
const language = currentLanguage;
|
||||
const sourceLabel = language === 'pt' ? 'FONTE OFICIAL ↗' : 'OFFICIAL SOURCE ↗';
|
||||
const kindLabels = language === 'pt' ? { STRONG: 'FORTE', BALANCED: 'EQUILÍBRIO', FAST: 'RÁPIDO' } : {};
|
||||
const tiers = item.tiers.map(([kind, name, note]) => `<div><span>${kindLabels[kind] || kind}</span><strong>${name}</strong><small>${note[language]}</small></div>`).join('');
|
||||
document.querySelector('#provider-detail').innerHTML = `<header><span>${item.label}</span><a href="${item.source}" target="_blank" rel="noopener">${sourceLabel}</a></header><h3>${item.title[language]}</h3><p>${item.copy[language]}</p><div class="model-ladder">${tiers}</div>`;
|
||||
selectButtons('[data-model-provider]', id, 'modelProvider');
|
||||
}
|
||||
|
||||
function renderEffort(id) {
|
||||
const item = modelGuide.efforts[id][currentLanguage];
|
||||
const provider = modelGuide.providers[document.querySelector('[data-model-provider].active')?.dataset.modelProvider || 'openai'];
|
||||
document.querySelector('#effort-detail').innerHTML = `<span>${item[0]}</span><p>${item[1]}</p><code>${provider.config}</code>`;
|
||||
selectButtons('[data-effort]', id, 'effort');
|
||||
}
|
||||
|
||||
function renderSkillFile(id) {
|
||||
const item = interactiveCopy.skillFiles[id];
|
||||
document.querySelector('#skill-detail').innerHTML = `<span>${item.icon}</span><div><strong>${item.title}</strong><p>${item[currentLanguage]}</p><small>${currentLanguage === 'pt' ? 'clique em outro arquivo para explorar' : 'select another file to explore'}</small></div>`;
|
||||
@@ -143,17 +329,56 @@ function renderCommonSkill(id) {
|
||||
const labels = language === 'pt'
|
||||
? ['QUANDO USAR', 'EXEMPLO', 'CUIDADO']
|
||||
: ['WHEN TO USE', 'EXAMPLE', 'WATCH OUT'];
|
||||
document.querySelector('#common-skill-detail').innerHTML = `<header><span>${item.number}</span><small>${item.kind[language]}</small></header><h3>${item.title}</h3><blockquote>${item.rule[language]}</blockquote><div class="common-skill-notes"><div><span>${labels[0]}</span><p>${item.use[language]}</p></div><div><span>${labels[1]}</span><p>${item.example[language]}</p></div><div><span>${labels[2]}</span><p>${item.caution[language]}</p></div></div>`;
|
||||
const sourceLabel = language === 'pt' ? 'FONTE NO GITHUB ↗' : 'GITHUB SOURCE ↗';
|
||||
document.querySelector('#common-skill-detail').innerHTML = `<header><span>${item.number}</span><small>${item.kind[language]}</small></header><h3>${item.title}</h3><blockquote>${item.rule[language]}</blockquote><div class="common-skill-notes"><div><span>${labels[0]}</span><p>${item.use[language]}</p></div><div><span>${labels[1]}</span><p>${item.example[language]}</p></div><div><span>${labels[2]}</span><p>${item.caution[language]}</p></div></div><a class="skill-source" href="${skillSources[id]}" target="_blank" rel="noopener">${sourceLabel}</a>`;
|
||||
selectButtons('[data-common-skill]', id, 'commonSkill');
|
||||
}
|
||||
|
||||
function renderHandsOn() {
|
||||
document.querySelector('#prompt-basic').textContent = handsOnPrompts[currentLanguage].basic;
|
||||
document.querySelector('#prompt-skills').textContent = handsOnPrompts[currentLanguage].skills;
|
||||
document.querySelector('#prompt-install-skills').textContent = skillInstallPrompts[currentLanguage];
|
||||
document.querySelectorAll('[data-copy-target] span').forEach((label) => { label.textContent = currentLanguage === 'pt' ? 'COPIAR' : 'COPY'; });
|
||||
}
|
||||
|
||||
async function copyPrompt(button) {
|
||||
const text = document.querySelector(`#${button.dataset.copyTarget}`).textContent;
|
||||
let copied = false;
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
copied = true;
|
||||
} catch (error) {
|
||||
const helper = document.createElement('textarea');
|
||||
helper.value = text;
|
||||
helper.setAttribute('readonly', '');
|
||||
helper.style.position = 'fixed';
|
||||
helper.style.opacity = '0';
|
||||
document.body.appendChild(helper);
|
||||
helper.select();
|
||||
copied = document.execCommand('copy');
|
||||
helper.remove();
|
||||
}
|
||||
const status = document.querySelector('#copy-status');
|
||||
status.textContent = copied
|
||||
? (currentLanguage === 'pt' ? 'Prompt copiado. Cole em uma nova sessão de agente.' : 'Prompt copied. Paste it into a fresh agent session.')
|
||||
: (currentLanguage === 'pt' ? 'Não foi possível copiar. Selecione o texto manualmente.' : 'Copy unavailable. Select the text manually.');
|
||||
if (copied) {
|
||||
button.classList.add('copied');
|
||||
button.querySelector('span').textContent = currentLanguage === 'pt' ? 'COPIADO' : 'COPIED';
|
||||
window.setTimeout(() => { button.classList.remove('copied'); button.querySelector('span').textContent = currentLanguage === 'pt' ? 'COPIAR' : 'COPY'; }, 1800);
|
||||
}
|
||||
}
|
||||
|
||||
function renderInteractive() {
|
||||
renderWorker(document.querySelector('[data-worker].active')?.dataset.worker || 'ui');
|
||||
renderTree(document.querySelector('[data-tree].active')?.dataset.tree || 'main');
|
||||
renderRoute(document.querySelector('[data-route].active')?.dataset.route || 'plan');
|
||||
renderModelProvider(document.querySelector('[data-model-provider].active')?.dataset.modelProvider || 'openai');
|
||||
renderEffort(document.querySelector('[data-effort].active')?.dataset.effort || 'medium');
|
||||
renderSkillFile(document.querySelector('[data-skill-file].active')?.dataset.skillFile || 'skill');
|
||||
renderSkillWorkflow(document.querySelector('[data-skill-step].active')?.dataset.skillStep || 'observe');
|
||||
renderCommonSkill(document.querySelector('[data-common-skill].active')?.dataset.commonSkill || 'ponytail');
|
||||
renderHandsOn();
|
||||
}
|
||||
|
||||
function applyLanguage(language) {
|
||||
@@ -172,9 +397,12 @@ document.querySelectorAll('[data-lang]').forEach((button) => button.addEventList
|
||||
document.querySelectorAll('[data-worker]').forEach((button) => button.addEventListener('click', () => renderWorker(button.dataset.worker)));
|
||||
document.querySelectorAll('[data-tree]').forEach((button) => button.addEventListener('click', () => renderTree(button.dataset.tree)));
|
||||
document.querySelectorAll('[data-route]').forEach((button) => button.addEventListener('click', () => renderRoute(button.dataset.route)));
|
||||
document.querySelectorAll('[data-model-provider]').forEach((button) => button.addEventListener('click', () => { renderModelProvider(button.dataset.modelProvider); renderEffort(document.querySelector('[data-effort].active')?.dataset.effort || 'medium'); }));
|
||||
document.querySelectorAll('[data-effort]').forEach((button) => button.addEventListener('click', () => renderEffort(button.dataset.effort)));
|
||||
document.querySelectorAll('[data-skill-file]').forEach((button) => button.addEventListener('click', () => renderSkillFile(button.dataset.skillFile)));
|
||||
document.querySelectorAll('[data-skill-step]').forEach((button) => button.addEventListener('click', () => renderSkillWorkflow(button.dataset.skillStep)));
|
||||
document.querySelectorAll('[data-common-skill]').forEach((button) => button.addEventListener('click', () => renderCommonSkill(button.dataset.commonSkill)));
|
||||
document.querySelectorAll('[data-copy-target]').forEach((button) => button.addEventListener('click', () => copyPrompt(button)));
|
||||
window.addEventListener('scroll', () => { const height = document.documentElement.scrollHeight - window.innerHeight; document.querySelector('.reading-progress span').style.width = `${height > 0 ? (window.scrollY / height) * 100 : 0}%`; }, { passive: true });
|
||||
|
||||
let savedLanguage = 'en';
|
||||
|
||||
Reference in New Issue
Block a user