feat(i18n): add AI history components internationalization support

- Add useI18n composable and ai-history locale files
- Refactor 10 AI history Vue components to support i18n (GPTEvolutionDemo, AIErasComparisonDemo, AiEvolutionDemo, etc.)
- Add English version of AI history appendix article
- Add English translations for stage-1 appendix-articles:
  - vibe-coding-tools-snake-game-tutorial.md
  - vibe-coding-tools-build-website-with-ai-coding-and-design-agents.md
- Use relative paths to reference Chinese version images
- Update appendix sidebar config to use English AI history link
This commit is contained in:
sanbuphy
2026-02-26 09:33:06 +08:00
parent d2809706e5
commit de86489421
17 changed files with 1451 additions and 140 deletions
@@ -2,28 +2,28 @@
<div class="demo-card">
<div class="era-container">
<div class="era-header">
🌟 AI 发展阶段与核心范式全景对比
{{ t('erasComparison.header') }}
</div>
<div class="era-grid">
<div v-for="era in eras" :key="era.name" class="era-item" :style="{ borderTopColor: era.color }">
<div class="e-icon" :style="{ background: era.color }">{{ era.icon }}</div>
<div class="e-name" :style="{ color: era.color }">{{ era.name }}</div>
<div v-for="(era, i) in localEras" :key="i" class="era-item" :style="{ borderTopColor: eraStyles[i]?.color }">
<div class="e-icon" :style="{ background: eraStyles[i]?.color }">{{ eraStyles[i]?.icon }}</div>
<div class="e-name" :style="{ color: eraStyles[i]?.color }">{{ era.name }}</div>
<div class="e-time">{{ era.time }}</div>
<div class="e-section">
<div class="e-label">驱动方式</div>
<div class="e-label">{{ t('erasComparison.driverLabel') }}</div>
<div class="e-value">{{ era.driver }}</div>
</div>
<div class="e-section">
<div class="e-label">核心机制</div>
<div class="e-label">{{ t('erasComparison.mechanismLabel') }}</div>
<div class="e-value">
<span class="highlight">{{ era.mechanism }}</span>
</div>
</div>
<div class="e-section">
<div class="e-label">典型代表</div>
<div class="e-label">{{ t('erasComparison.examplesLabel') }}</div>
<div class="e-tags">
<span v-for="tag in era.examples" :key="tag" class="e-tag">{{ tag }}</span>
</div>
@@ -35,52 +35,19 @@
</template>
<script setup>
const eras = [
{
name: '规则系统时代',
time: '1960s - 1980s',
icon: '📜',
color: '#059669', // emerald
driver: '人类硬编码知识',
mechanism: 'If-Then 逻辑推演',
examples: ['Dendral', '深蓝 (Deep Blue)']
},
{
name: '传统机器学习',
time: '1990s - 2000s',
icon: '📊',
color: '#d97706', // amber
driver: '人工特征工程 + 统计学',
mechanism: '寻找数学决策边界',
examples: ['支持向量机 (SVM)', '随机森林']
},
{
name: '深度学习革命',
time: '2010s',
icon: '🧠',
color: '#dc2626', // red
driver: '大数据 + 算力爬升',
mechanism: '神经网络自动提取特征',
examples: ['AlexNet (CNN)', 'AlphaGo (RL)']
},
{
name: '大语言模型 (LLM)',
time: '2018 - 至今',
icon: '💬',
color: '#7c3aed', // violet
driver: '海量无标注数据 + 暴力计算',
mechanism: '预测下一个词 + 涌现常识',
examples: ['GPT-4', 'Claude 3']
},
{
name: '智能体 (Agentic AI)',
time: '现在 - 未来',
icon: '🤖',
color: '#0284c7', // light blue
driver: '大模型大脑 + 环境感知',
mechanism: '自主规划 + 工具调用',
examples: ['AI 程序员', '具身智能']
}
import { computed } from 'vue'
import { useI18n } from '../../../composables/useI18n.js'
import { aiHistoryLocale } from '../../../locales/ai-history/index.js'
const { t, messages } = useI18n(aiHistoryLocale)
const localEras = computed(() => messages.value.erasComparison?.eras ?? [])
const eraStyles = [
{ icon: '📜', color: '#059669' },
{ icon: '📊', color: '#d97706' },
{ icon: '🧠', color: '#dc2626' },
{ icon: '💬', color: '#7c3aed' },
{ icon: '🤖', color: '#0284c7' },
]
</script>
@@ -1,29 +1,36 @@
<template>
<div class="demo-card">
<div class="timeline-visual">
<div v-for="era in eras" :key="era.label" class="era" :style="{ flex: era.flex, background: era.bg }">
<div class="era-label">{{ era.label }}</div>
<div class="era-years">{{ era.years }}</div>
<div v-for="(era, i) in eras" :key="i" class="era" :style="{ flex: era.flex, background: era.bg }">
<div class="era-label">{{ localeEras[i]?.label ?? era.label }}</div>
<div class="era-years">{{ localeEras[i]?.years ?? era.years }}</div>
</div>
</div>
<div class="legend">
<span class="legend-item"><span class="dot" style="background:#059669"></span>技术浪潮</span>
<span class="legend-item"><span class="dot" style="background:#94a3b8"></span> AI 寒冬</span>
<span class="legend-item"><span class="dot" style="background:#7c3aed"></span>大模型时代</span>
<span class="legend-item"><span class="dot" style="background:#059669"></span>{{ t('aiEvolution.legend.wave') }}</span>
<span class="legend-item"><span class="dot" style="background:#94a3b8"></span>{{ t('aiEvolution.legend.winter') }}</span>
<span class="legend-item"><span class="dot" style="background:#7c3aed"></span>{{ t('aiEvolution.legend.llm') }}</span>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useI18n } from '../../../composables/useI18n.js'
import { aiHistoryLocale } from '../../../locales/ai-history/index.js'
const { t, messages } = useI18n(aiHistoryLocale)
const localeEras = computed(() => messages.value.aiEvolution?.eras ?? [])
const eras = [
{ label: '理论奠基', years: '1940s-50s', flex: 1.5, bg: 'linear-gradient(135deg, #dbeafe, #bfdbfe)' },
{ label: '第一次浪潮', years: '1960s-70s', flex: 1.5, bg: 'linear-gradient(135deg, #d1fae5, #a7f3d0)' },
{ label: '❄️ 寒冬 I', years: '1974-80', flex: 0.7, bg: 'linear-gradient(135deg, #e2e8f0, #cbd5e1)' },
{ label: '第二次浪潮', years: '1980s', flex: 1, bg: 'linear-gradient(135deg, #d1fae5, #a7f3d0)' },
{ label: '❄️ 寒冬 II', years: '1987-93', flex: 0.7, bg: 'linear-gradient(135deg, #e2e8f0, #cbd5e1)' },
{ label: 'ML 崛起', years: '1990s-2000s', flex: 1.5, bg: 'linear-gradient(135deg, #d1fae5, #6ee7b7)' },
{ label: '深度学习', years: '2010s', flex: 1.2, bg: 'linear-gradient(135deg, #a7f3d0, #34d399)' },
{ label: '大模型时代', years: '2018+', flex: 1.2, bg: 'linear-gradient(135deg, #c4b5fd, #a78bfa)' },
{ flex: 1.5, bg: 'linear-gradient(135deg, #dbeafe, #bfdbfe)' },
{ flex: 1.5, bg: 'linear-gradient(135deg, #d1fae5, #a7f3d0)' },
{ flex: 0.7, bg: 'linear-gradient(135deg, #e2e8f0, #cbd5e1)' },
{ flex: 1, bg: 'linear-gradient(135deg, #d1fae5, #a7f3d0)' },
{ flex: 0.7, bg: 'linear-gradient(135deg, #e2e8f0, #cbd5e1)' },
{ flex: 1.5, bg: 'linear-gradient(135deg, #d1fae5, #6ee7b7)' },
{ flex: 1.2, bg: 'linear-gradient(135deg, #a7f3d0, #34d399)' },
{ flex: 1.2, bg: 'linear-gradient(135deg, #c4b5fd, #a78bfa)' },
]
</script>
@@ -2,7 +2,7 @@
<div class="demo-card">
<div class="attention-layout">
<div class="sentence-col">
<div class="col-label">处理<strong></strong>时的注意力分配</div>
<div class="col-label" v-html="colLabel"></div>
<div class="sentence-box">
<span v-for="(word, i) in sentence" :key="i" class="word-token" :class="{ focus: i === focusIdx }">{{ word }}</span>
</div>
@@ -18,16 +18,26 @@
</div>
</div>
<div class="caption">
虽在句中间模型却把 65% 注意力精准投向句首的小明跨越距离识别代词指代
{{ t('attention.caption') }}
</div>
</div>
</template>
<script setup>
const sentence = ['小明', '把', '苹果', '给了', '他', '的', '母亲']
const focusIdx = 4
const attn = [0.65, 0.05, 0.10, 0.10, 0.05, 0.03, 0.02]
const weights = sentence.map((word, i) => ({ word, w: attn[i] }))
import { computed } from 'vue'
import { useI18n } from '../../../composables/useI18n.js'
import { aiHistoryLocale } from '../../../locales/ai-history/index.js'
const { t, messages } = useI18n(aiHistoryLocale)
const attnData = computed(() => messages.value.attention ?? {})
const sentence = computed(() => attnData.value.sentence ?? [])
const focusIdx = computed(() => attnData.value.focusIdx ?? 4)
const rawWeights = computed(() => attnData.value.weights ?? [])
const weights = computed(() => sentence.value.map((word, i) => ({ word, w: rawWeights.value[i] ?? 0 })))
const focusWord = computed(() => sentence.value[focusIdx.value] ?? '')
const colLabel = computed(() => (attnData.value.colLabel ?? '').replace('{word}', focusWord.value))
const barColor = (v) => v > 0.5 ? '#dc2626' : v > 0.15 ? '#d97706' : v > 0.06 ? '#059669' : 'var(--vp-c-divider)'
</script>
@@ -1,15 +1,15 @@
<template>
<div class="demo-card">
<div class="bp-flow">
<div v-for="(step, i) in steps" :key="i" class="step-block" :style="{ borderTopColor: step.color }">
<div class="step-num" :style="{ background: step.color }">{{ i + 1 }}</div>
<div v-for="(step, i) in localSteps" :key="i" class="step-block" :style="{ borderTopColor: stepColors[i] }">
<div class="step-num" :style="{ background: stepColors[i] }">{{ i + 1 }}</div>
<div class="step-icon">{{ step.icon }}</div>
<div class="step-name">{{ step.name }}</div>
<div class="step-desc">{{ step.desc }}</div>
</div>
</div>
<div class="loss-visual">
<div class="loss-label">Loss误差随训练轮次下降</div>
<div class="loss-label">{{ t('backprop.lossLabel') }}</div>
<svg viewBox="0 0 320 130" class="loss-svg">
<!-- Axes -->
<line x1="40" y1="110" x2="300" y2="110" stroke="var(--vp-c-text-3)" stroke-width="1.5" />
@@ -21,12 +21,12 @@
<polygon points="37,15 40,10 43,15" fill="var(--vp-c-text-3)" />
<!-- Y Label -->
<text x="30" y="25" text-anchor="end" class="ax-text"></text>
<text x="30" y="105" text-anchor="end" class="ax-text"></text>
<text x="30" y="25" text-anchor="end" class="ax-text">{{ t('backprop.axisHigh') }}</text>
<text x="30" y="105" text-anchor="end" class="ax-text">{{ t('backprop.axisLow') }}</text>
<text x="20" y="65" text-anchor="middle" transform="rotate(-90 20 65)" class="ax-title">Loss</text>
<!-- X Label -->
<text x="300" y="125" text-anchor="end" class="ax-title">训练轮次 (Epochs)</text>
<text x="300" y="125" text-anchor="end" class="ax-title">{{ t('backprop.axisEpochs') }}</text>
<!-- Loss 曲线 -->
<polyline :points="lossPoints" fill="none" stroke="var(--vp-c-brand)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
@@ -36,12 +36,14 @@
</template>
<script setup>
const steps = [
{ icon: '➡️', name: '前向传播', desc: '数据流过网络,得出预测', color: '#3b82f6' },
{ icon: '📐', name: '计算误差', desc: '预测值 vs 正确答案,算 Loss', color: '#d97706' },
{ icon: '⬅️', name: '反向传播', desc: '逐层追溯每个权重的"责任"', color: '#dc2626' },
{ icon: '⚙️', name: '更新权重', desc: '按责任微调,减少下次误差', color: '#059669' },
]
import { computed } from 'vue'
import { useI18n } from '../../../composables/useI18n.js'
import { aiHistoryLocale } from '../../../locales/ai-history/index.js'
const { t, messages } = useI18n(aiHistoryLocale)
const localSteps = computed(() => messages.value.backprop?.steps ?? [])
const stepColors = ['#3b82f6', '#d97706', '#dc2626', '#059669']
const lossPoints = (() => {
const pts = []
for (let i = 0; i <= 50; i++) {
@@ -1,24 +1,31 @@
<template>
<div class="demo-card">
<div class="schools-grid">
<div v-for="s in schools" :key="s.name" class="school-card" :style="{ borderTopColor: s.color }">
<div v-for="(s, i) in schoolStyles" :key="i" class="school-card" :style="{ borderTopColor: s.color }">
<div class="card-head">
<span class="school-icon">{{ s.icon }}</span>
<span class="school-name" :style="{ color: s.color }">{{ s.name }}</span>
<span class="school-name" :style="{ color: s.color }">{{ localeItems[i]?.name }}</span>
</div>
<div class="school-idea">{{ s.idea }}</div>
<div class="school-rep">代表{{ s.rep }}</div>
<div class="school-status">{{ s.status }}</div>
<div class="school-idea">{{ localeItems[i]?.idea }}</div>
<div class="school-rep">{{ t('schools.repLabel') }}{{ localeItems[i]?.rep }}</div>
<div class="school-status">{{ localeItems[i]?.status }}</div>
</div>
</div>
</div>
</template>
<script setup>
const schools = [
{ name: '符号主义', icon: '📜', color: '#059669', idea: '智能 = 符号推理 / If-Then 规则', rep: '专家系统、深蓝', status: '→ 与连接主义融合(神经符号 AI)' },
{ name: '连接主义', icon: '🧠', color: '#7c3aed', idea: '智能 = 神经元网络 + 海量数据', rep: 'AlphaGo、GPT 系列', status: '→ 主导大模型时代,当前主流' },
{ name: '行为主义', icon: '🎮', color: '#d97706', idea: '智能 = 与环境互动 / 强化学习', rep: 'AlphaGoRL 部分)', status: '→ 与连接主义融合(深度强化学习)' },
import { computed } from 'vue'
import { useI18n } from '../../../composables/useI18n.js'
import { aiHistoryLocale } from '../../../locales/ai-history/index.js'
const { t, messages } = useI18n(aiHistoryLocale)
const localeItems = computed(() => messages.value.schools?.items ?? [])
const schoolStyles = [
{ icon: '📜', color: '#059669' },
{ icon: '🧠', color: '#7c3aed' },
{ icon: '🎮', color: '#d97706' },
]
</script>
@@ -1,18 +1,27 @@
<template>
<div class="demo-card">
<div class="demo-label">符号主义的核心思路 把知识写成规则</div>
<div class="demo-label">{{ t('foundation.label') }}</div>
<div class="code-block">
<div class="code-line"><span class="kw">IF</span> 体温 &gt; 38.5°C <span class="kw">AND</span> 白细胞计数 &gt; 11000</div>
<div class="code-line indent"><span class="kw">THEN</span> 诊断 = <span class="str">"细菌感染"</span></div>
<div class="code-line"><span class="kw">IF</span> 诊断 = <span class="str">"细菌感染"</span> <span class="kw">AND</span> 对青霉素不过敏</div>
<div class="code-line indent"><span class="kw">THEN</span> 治疗方案 = <span class="str">"青霉素 400mg / 每日两次"</span></div>
<div class="code-line comment">// 早期医疗专家系统(MYCIN,1977)就是由 450+ 条这样的规则组成的</div>
<div v-for="(line, i) in foundationLines" :key="i" class="code-line" :class="{ indent: line.indent }">
<template v-for="(p, j) in line.parts" :key="j">
<span v-if="p.kw" class="kw">{{ p.kw }}</span>
<span v-else-if="p.str" class="str">{{ p.str }}</span>
<template v-else>{{ p.text }}</template>
</template>
</div>
<div class="code-line comment">{{ t('foundation.comment') }}</div>
</div>
<div class="demo-caption">人类专家把经验翻译成一条条 IF-THEN 规则机器逐条匹配执行</div>
<div class="demo-caption">{{ t('foundation.caption') }}</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useI18n } from '../../../composables/useI18n.js'
import { aiHistoryLocale } from '../../../locales/ai-history/index.js'
const { t, messages } = useI18n(aiHistoryLocale)
const foundationLines = computed(() => messages.value.foundation?.lines ?? [])
</script>
<style scoped>
@@ -1,14 +1,14 @@
<template>
<div class="demo-card">
<div class="gpt-grid">
<div v-for="m in models" :key="m.name" class="gpt-card" :style="{ borderTopColor: m.color }">
<div v-for="(m, i) in models" :key="i" class="gpt-card" :style="{ borderTopColor: modelColors[i] }">
<div class="card-top">
<span class="gpt-name" :style="{ color: m.color }">{{ m.name }}</span>
<span class="gpt-name" :style="{ color: modelColors[i] }">{{ m.name }}</span>
<span class="gpt-year">{{ m.year }}</span>
</div>
<div class="param-val">{{ m.params }}</div>
<div class="param-bar-bg">
<div class="param-bar" :style="{ width: m.barWidth, background: m.color }"></div>
<div class="param-bar" :style="{ width: m.barWidth, background: modelColors[i] }"></div>
</div>
<div class="gpt-key">{{ m.key }}</div>
</div>
@@ -17,12 +17,14 @@
</template>
<script setup>
const models = [
{ name: 'GPT-1', year: '2018', params: '1.17 亿', barWidth: '2%', color: '#94a3b8', key: '预训练+微调范式确立' },
{ name: 'GPT-2', year: '2019', params: '15 亿', barWidth: '6%', color: '#3b82f6', key: 'Zero-shot 零样本泛化' },
{ name: 'GPT-3', year: '2020', params: '1750 亿', barWidth: '45%', color: '#7c3aed', key: '⚡ 涌现!上下文学习' },
{ name: 'GPT-4', year: '2023', params: '~1.8 万亿', barWidth: '100%', color: '#dc2626', key: '多模态 + 复杂推理' },
]
import { computed } from 'vue'
import { useI18n } from '../../../composables/useI18n.js'
import { aiHistoryLocale } from '../../../locales/ai-history/index.js'
const { messages } = useI18n(aiHistoryLocale)
const models = computed(() => messages.value.gptEvolution ?? [])
const modelColors = ['#94a3b8', '#3b82f6', '#7c3aed', '#dc2626']
</script>
<style scoped>
@@ -7,12 +7,12 @@
<g v-for="layer in layers" :key="layer.idx">
<circle v-for="n in layer.nodes" :key="n.id" :cx="n.x" :cy="n.y" r="15" :fill="layer.fill" :stroke="layer.stroke" stroke-width="2" />
</g>
<text v-for="layer in layers" :key="'l-'+layer.idx" :x="layer.x" y="194" text-anchor="middle" :fill="layer.stroke" class="lbl">{{ layer.name }}</text>
<text v-for="(layer, i) in layers" :key="'l-'+layer.idx" :x="layer.x" y="194" text-anchor="middle" :fill="layer.stroke" class="lbl">{{ localLayers[i]?.name }}</text>
</svg>
</div>
<div class="layer-cards">
<div v-for="info in layerInfo" :key="info.name" class="layer-card" :style="{ borderLeftColor: info.color }">
<div class="lc-title" :style="{ color: info.color }">{{ info.name }}</div>
<div v-for="(info, i) in localLayers" :key="i" class="layer-card" :style="{ borderLeftColor: layerColors[i]?.color }">
<div class="lc-title" :style="{ color: layerColors[i]?.color }">{{ info.name }}</div>
<div class="lc-desc">{{ info.desc }}</div>
</div>
</div>
@@ -21,27 +21,33 @@
</template>
<script setup>
import { computed } from 'vue'
import { useI18n } from '../../../composables/useI18n.js'
import { aiHistoryLocale } from '../../../locales/ai-history/index.js'
const { messages } = useI18n(aiHistoryLocale)
const localLayers = computed(() => messages.value.neuralNet?.layers ?? [])
const W = 380, H = 185
const layerDef = [
{ name: '输入层', count: 3, xFrac: 0.13, color: '#3b82f6', fill: '#dbeafe' },
{ name: '隐藏层', count: 4, xFrac: 0.5, color: '#7c3aed', fill: '#ede9fe' },
{ name: '输出层', count: 2, xFrac: 0.87, color: '#059669', fill: '#d1fae5' },
const layerColors = [
{ color: '#3b82f6', fill: '#dbeafe' },
{ color: '#7c3aed', fill: '#ede9fe' },
{ color: '#059669', fill: '#d1fae5' },
]
const layers = layerDef.map((l, idx) => {
const x = l.xFrac * W
const gap = Math.min(46, (H - 36) / (l.count - 1 || 1))
const startY = (H - gap * (l.count - 1)) / 2
return { idx, x, name: l.name, fill: l.fill, stroke: l.color, nodes: Array.from({ length: l.count }, (_, i) => ({ id: `${idx}-${i}`, x, y: startY + i * gap })) }
const layerCounts = [3, 4, 2]
const xFracs = [0.13, 0.5, 0.87]
const layers = layerColors.map((l, idx) => {
const x = xFracs[idx] * W
const count = layerCounts[idx]
const gap = Math.min(46, (H - 36) / (count - 1 || 1))
const startY = (H - gap * (count - 1)) / 2
return { idx, x, fill: l.fill, stroke: l.color, nodes: Array.from({ length: count }, (_, i) => ({ id: `${idx}-${i}`, x, y: startY + i * gap })) }
})
const connections = []
for (let li = 0; li < layers.length - 1; li++) {
layers[li].nodes.forEach(a => { layers[li + 1].nodes.forEach(b => { connections.push({ id: `${a.id}-${b.id}`, x1: a.x, y1: a.y, x2: b.x, y2: b.y }) }) })
}
const layerInfo = [
{ name: '输入层', desc: '原始像素 / 数值信号', color: '#3b82f6' },
{ name: '隐藏层(可叠加多层)', desc: '底层识别边缘 → 中层识别形状 → 高层识别语义概念', color: '#7c3aed' },
{ name: '输出层', desc: '最终分类或预测结果', color: '#059669' },
]
</script>
<style scoped>
@@ -2,13 +2,13 @@
<div class="demo-card">
<div class="perceptron-layout">
<div class="inputs-col">
<div v-for="inp in inputs" :key="inp.label" class="input-node">
<div v-for="(inp, i) in inputs" :key="i" class="input-node">
<span class="node-circle">{{ inp.val }}</span>
<span class="node-label">{{ inp.label }}</span>
<span class="node-label">{{ featureLabels[i] }}</span>
</div>
</div>
<div class="weights-col">
<div v-for="inp in inputs" :key="inp.label" class="weight-arrow">
<div v-for="(inp, i) in inputs" :key="i" class="weight-arrow">
<span class="arrow"></span>
<span class="w-tag">×{{ inp.weight }}</span>
</div>
@@ -18,7 +18,7 @@
<div class="n-sym">Σ</div>
<div class="n-val">{{ sum }}</div>
</div>
<span class="bias-tag">偏置 {{ bias }}</span>
<span class="bias-tag">{{ t('perceptron.biasLabel') }} {{ bias }}</span>
</div>
<div class="act-col">
<span class="arrow big"></span>
@@ -28,19 +28,23 @@
<div class="output-col">
<div class="output-node" :class="{ on: output === 1 }">
<span class="out-val">{{ output }}</span>
<span class="out-lbl">{{ output ? '激活' : '静默' }}</span>
<span class="out-lbl">{{ output ? t('perceptron.activated') : t('perceptron.silent') }}</span>
</div>
</div>
</div>
<div class="caption">
输入特征&emsp; 乘以权重重要性&emsp; 求和 + 偏置&emsp; 超过阈值就激活输出 1否则输出 0
</div>
<div class="caption">{{ t('perceptron.caption') }}</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
const inputs = [{ label: '特征 x₁', val: 1, weight: 0.6 }, { label: '特征 x₂', val: 0, weight: 0.4 }]
import { useI18n } from '../../../composables/useI18n.js'
import { aiHistoryLocale } from '../../../locales/ai-history/index.js'
const { t, messages } = useI18n(aiHistoryLocale)
const featureLabels = computed(() => messages.value.perceptron?.features ?? [])
const inputs = [{ val: 1, weight: 0.6 }, { val: 0, weight: 0.4 }]
const bias = -0.3
const sum = computed(() => Number((inputs.reduce((s, i) => s + i.val * i.weight, 0) + bias).toFixed(2)))
const output = computed(() => sum.value > 0 ? 1 : 0)