代码块

让长代码易读、可查、可复制。行号、换行与折叠安静地留在手边。

src/boot.tsshiki 双主题高亮
import { renderChart } from '@icen.ai/ui/behaviors/charts';import { initTabs } from '@icen.ai/ui/behaviors/tabs';import { initCopy } from '@icen.ai/ui/behaviors/copy';const chart = renderChart(el, {  type: 'line',  labels: ['v0.8', 'v0.9', 'v0.10', 'v0.11'],  series: [{ name: '组件', values: [58, 96, 117, 120] }],});initTabs();initCopy();
bash超 30 行自动折叠
pipeline.step(1)  # 流水线第 1 步pipeline.step(2)  # 流水线第 2 步pipeline.step(3)  # 流水线第 3 步pipeline.step(4)  # 流水线第 4 步pipeline.step(5)  # 流水线第 5 步pipeline.step(6)  # 流水线第 6 步pipeline.step(7)  # 流水线第 7 步pipeline.step(8)  # 流水线第 8 步pipeline.step(9)  # 流水线第 9 步pipeline.step(10)  # 流水线第 10 步pipeline.step(11)  # 流水线第 11 步pipeline.step(12)  # 流水线第 12 步pipeline.step(13)  # 流水线第 13 步pipeline.step(14)  # 流水线第 14 步pipeline.step(15)  # 流水线第 15 步pipeline.step(16)  # 流水线第 16 步pipeline.step(17)  # 流水线第 17 步pipeline.step(18)  # 流水线第 18 步pipeline.step(19)  # 流水线第 19 步pipeline.step(20)  # 流水线第 20 步pipeline.step(21)  # 流水线第 21 步pipeline.step(22)  # 流水线第 22 步pipeline.step(23)  # 流水线第 23 步pipeline.step(24)  # 流水线第 24 步pipeline.step(25)  # 流水线第 25 步pipeline.step(26)  # 流水线第 26 步pipeline.step(27)  # 流水线第 27 步pipeline.step(28)  # 流水线第 28 步pipeline.step(29)  # 流水线第 29 步pipeline.step(30)  # 流水线第 30 步pipeline.step(31)  # 流水线第 31 步pipeline.step(32)  # 流水线第 32 步pipeline.step(33)  # 流水线第 33 步pipeline.step(34)  # 流水线第 34 步pipeline.step(35)  # 流水线第 35 步pipeline.step(36)  # 流水线第 36 步pipeline.step(37)  # 流水线第 37 步pipeline.step(38)  # 流水线第 38 步pipeline.step(39)  # 流水线第 39 步pipeline.step(40)  # 流水线第 40 步pipeline.step(41)  # 流水线第 41 步pipeline.step(42)  # 流水线第 42 步
diffdiff:旧/新双行号
@@ -1,4 +1,6 @@ import { h } from "./ai-core";-const version = "0.10.0";+const version = "0.11.0";+const codeBlock = true;  export function boot() {   return h("div", "", version); }
html
<figure class="icen-code-block">  <figcaption class="icen-code-block-head"><span class="icen-code-block-id"><span class="icen-code-block-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M6 2.5h5.5L16 7v9.5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-12a2 2 0 0 1 2-2ZM11.5 2.5V7H16"></path></svg></span><span class="icen-code-block-file">src/boot.ts</span><span class="icen-code-block-title">shiki 双主题高亮</span></span><span class="icen-code-block-actions"><button type="button" data-icen-code-block-num data-icen-tooltip="隐藏行号" aria-label="隐藏行号" aria-pressed="true" class="icen-code-block-btn is-on"><span class="icen-code-block-btn-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M3 5h2M3 10h2M3 15h2M8 5h9M8 10h9M8 15h9"></path></svg></span></button><button type="button" data-icen-code-block-wrap data-icen-tooltip="自动换行" aria-label="自动换行" aria-pressed="false" class="icen-code-block-btn"><span class="icen-code-block-btn-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M3 5h14M3 10h10a3 3 0 0 1 0 6h-3M12.5 13.5 10 16l2.5 2.5"></path></svg></span></button><button type="button" data-icen-code-block-copy data-icen-tooltip="复制" aria-label="复制" class="icen-code-block-btn"><span class="icen-code-block-btn-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M7 7h8a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2V7ZM13 7V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2"></path></svg></span><span class="icen-code-block-btn-check"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M4.5 10.2l3.6 3.6 7.4-7.6"></path></svg></span></button></span></figcaption>  <pre class="shiki shiki-themes github-light-default github-dark-default" style="color:#1f2328;--shiki-dark:#e6edf3" data-icen-dual-theme tabindex="0" role="region" aria-label="src/boot.ts"><code><span class="icen-code-block-line"><span style="color:#c7212c;--shiki-dark:#ff867d">import</span><span style="color:#1f2328;--shiki-dark:#e6edf3"> { renderChart } </span><span style="color:#c7212c;--shiki-dark:#ff867d">from</span><span style="color:#0a3069;--shiki-dark:#a5d6ff"> '@icen.ai/ui/behaviors/charts'</span><span style="color:#1f2328;--shiki-dark:#e6edf3">;</span></span><span class="icen-code-block-line"><span style="color:#c7212c;--shiki-dark:#ff867d">import</span><span style="color:#1f2328;--shiki-dark:#e6edf3"> { initTabs } </span><span style="color:#c7212c;--shiki-dark:#ff867d">from</span><span style="color:#0a3069;--shiki-dark:#a5d6ff"> '@icen.ai/ui/behaviors/tabs'</span><span style="color:#1f2328;--shiki-dark:#e6edf3">;</span></span><span class="icen-code-block-line"><span style="color:#c7212c;--shiki-dark:#ff867d">import</span><span style="color:#1f2328;--shiki-dark:#e6edf3"> { initCopy } </span><span style="color:#c7212c;--shiki-dark:#ff867d">from</span><span style="color:#0a3069;--shiki-dark:#a5d6ff"> '@icen.ai/ui/behaviors/copy'</span><span style="color:#1f2328;--shiki-dark:#e6edf3">;</span></span><span class="icen-code-block-line"></span><span class="icen-code-block-line icen-code-block-line--hl"><span style="color:#c7212c;--shiki-dark:#ff867d">const</span><span style="color:#0550ae;--shiki-dark:#79c0ff"> chart</span><span style="color:#c7212c;--shiki-dark:#ff867d"> =</span><span style="color:#794ad0;--shiki-dark:#d2a8ff"> renderChart</span><span style="color:#1f2328;--shiki-dark:#e6edf3">(el, {</span></span><span class="icen-code-block-line"><span style="color:#1f2328;--shiki-dark:#e6edf3">  type: </span><span style="color:#0a3069;--shiki-dark:#a5d6ff">'line'</span><span style="color:#1f2328;--shiki-dark:#e6edf3">,</span></span><span class="icen-code-block-line"><span style="color:#1f2328;--shiki-dark:#e6edf3">  labels: [</span><span style="color:#0a3069;--shiki-dark:#a5d6ff">'v0.8'</span><span style="color:#1f2328;--shiki-dark:#e6edf3">, </span><span style="color:#0a3069;--shiki-dark:#a5d6ff">'v0.9'</span><span style="color:#1f2328;--shiki-dark:#e6edf3">, </span><span style="color:#0a3069;--shiki-dark:#a5d6ff">'v0.10'</span><span style="color:#1f2328;--shiki-dark:#e6edf3">, </span><span style="color:#0a3069;--shiki-dark:#a5d6ff">'v0.11'</span><span style="color:#1f2328;--shiki-dark:#e6edf3">],</span></span><span class="icen-code-block-line"><span style="color:#1f2328;--shiki-dark:#e6edf3">  series: [{ name: </span><span style="color:#0a3069;--shiki-dark:#a5d6ff">'组件'</span><span style="color:#1f2328;--shiki-dark:#e6edf3">, values: [</span><span style="color:#0550ae;--shiki-dark:#79c0ff">58</span><span style="color:#1f2328;--shiki-dark:#e6edf3">, </span><span style="color:#0550ae;--shiki-dark:#79c0ff">96</span><span style="color:#1f2328;--shiki-dark:#e6edf3">, </span><span style="color:#0550ae;--shiki-dark:#79c0ff">117</span><span style="color:#1f2328;--shiki-dark:#e6edf3">, </span><span style="color:#0550ae;--shiki-dark:#79c0ff">120</span><span style="color:#1f2328;--shiki-dark:#e6edf3">] }],</span></span><span class="icen-code-block-line"><span style="color:#1f2328;--shiki-dark:#e6edf3">});</span></span><span class="icen-code-block-line"><span style="color:#794ad0;--shiki-dark:#d2a8ff">initTabs</span><span style="color:#1f2328;--shiki-dark:#e6edf3">();</span></span><span class="icen-code-block-line"><span style="color:#794ad0;--shiki-dark:#d2a8ff">initCopy</span><span style="color:#1f2328;--shiki-dark:#e6edf3">();</span></span></code></pre></figure><div style="height:14px"></div><figure class="icen-code-block icen-code-block--nonum is-folded">  <figcaption class="icen-code-block-head"><span class="icen-code-block-id"><span class="icen-code-block-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M5 3.5h10a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2ZM6 7l3 3-3 3M11 13h3"></path></svg></span><span class="icen-code-block-lang">bash</span><span class="icen-code-block-title">超 30 行自动折叠</span></span><span class="icen-code-block-actions"><button type="button" data-icen-code-block-num data-icen-tooltip="显示行号" aria-label="显示行号" aria-pressed="false" class="icen-code-block-btn"><span class="icen-code-block-btn-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M3 5h2M3 10h2M3 15h2M8 5h9M8 10h9M8 15h9"></path></svg></span></button><button type="button" data-icen-code-block-wrap data-icen-tooltip="自动换行" aria-label="自动换行" aria-pressed="false" class="icen-code-block-btn"><span class="icen-code-block-btn-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M3 5h14M3 10h10a3 3 0 0 1 0 6h-3M12.5 13.5 10 16l2.5 2.5"></path></svg></span></button><button type="button" data-icen-code-block-fold data-icen-tooltip="展开代码" aria-label="展开代码" aria-expanded="false" class="icen-code-block-btn"><span class="icen-code-block-btn-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M5.5 7.75 10 12.25l4.5-4.5"></path></svg></span></button><button type="button" data-icen-code-block-copy data-icen-tooltip="复制" aria-label="复制" class="icen-code-block-btn"><span class="icen-code-block-btn-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M7 7h8a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2V7ZM13 7V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2"></path></svg></span><span class="icen-code-block-btn-check"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M4.5 10.2l3.6 3.6 7.4-7.6"></path></svg></span></button></span></figcaption>  <pre class="shiki shiki-themes github-light-default github-dark-default" style="color:#1f2328;--shiki-dark:#e6edf3" data-icen-dual-theme tabindex="0" role="region" aria-label="超 30 行自动折叠"><code><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(1</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 1 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(2</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 2 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(3</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 3 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(4</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 4 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(5</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 5 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(6</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 6 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(7</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 7 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(8</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 8 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(9</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 9 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(10</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 10 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(11</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 11 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(12</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 12 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(13</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 13 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(14</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 14 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(15</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 15 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(16</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 16 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(17</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 17 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(18</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 18 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(19</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 19 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(20</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 20 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(21</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 21 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(22</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 22 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(23</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 23 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(24</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 24 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(25</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 25 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(26</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 26 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(27</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 27 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(28</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 28 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(29</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 29 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(30</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 30 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(31</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 31 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(32</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 32 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(33</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 33 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(34</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 34 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(35</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 35 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(36</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 36 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(37</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 37 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(38</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 38 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(39</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 39 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(40</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 40 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(41</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 41 步</span></span><span class="icen-code-block-line"><span style="color:#953800;--shiki-dark:#ffa657">pipeline.step(42</span><span style="color:#1f2328;--shiki-dark:#e6edf3">)  </span><span style="color:#606770;--shiki-dark:#a3aab2"># 流水线第 42 步</span></span></code></pre>  <button type="button" class="icen-code-block-tail" aria-expanded="false" aria-label="共 42 行, 展开代码"><span class="icen-code-block-count">共 42 行</span><span class="icen-code-block-less">收起</span><span class="icen-code-block-tail-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M5.5 7.75 10 12.25l4.5-4.5"></path></svg></span></button></figure><div style="height:14px"></div><figure class="icen-code-block icen-code-block--diff">  <figcaption class="icen-code-block-head"><span class="icen-code-block-id"><span class="icen-code-block-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M6 6v8M14 6v2a4 4 0 0 1-4 4H6M8 4a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM8 16a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM16 4a2 2 0 1 1-4 0 2 2 0 0 1 4 0Z"></path></svg></span><span class="icen-code-block-lang">diff</span><span class="icen-code-block-title">diff:旧/新双行号</span></span><span class="icen-code-block-actions"><button type="button" data-icen-code-block-num data-icen-tooltip="隐藏行号" aria-label="隐藏行号" aria-pressed="true" class="icen-code-block-btn is-on"><span class="icen-code-block-btn-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M3 5h2M3 10h2M3 15h2M8 5h9M8 10h9M8 15h9"></path></svg></span></button><button type="button" data-icen-code-block-wrap data-icen-tooltip="自动换行" aria-label="自动换行" aria-pressed="false" class="icen-code-block-btn"><span class="icen-code-block-btn-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M3 5h14M3 10h10a3 3 0 0 1 0 6h-3M12.5 13.5 10 16l2.5 2.5"></path></svg></span></button><button type="button" data-icen-code-block-copy data-icen-tooltip="复制" aria-label="复制" class="icen-code-block-btn"><span class="icen-code-block-btn-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M7 7h8a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2V7ZM13 7V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2"></path></svg></span><span class="icen-code-block-btn-check"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false" class="icen-symbol" aria-hidden="true"><path d="M4.5 10.2l3.6 3.6 7.4-7.6"></path></svg></span></button></span></figcaption>  <pre tabindex="0" role="region" aria-label="diff:旧/新双行号"><code><span class="icen-code-block-line icen-code-block-line--meta">@@ -1,4 +1,6 @@</span><span class="icen-code-block-line icen-code-block-line--ctx" style="counter-set: icen-cb-dl-n 1"> import { h } from &quot;./ai-core&quot;;</span><span class="icen-code-block-line icen-code-block-line--del" style="counter-set: icen-cb-dl-o 2">-const version = &quot;0.10.0&quot;;</span><span class="icen-code-block-line icen-code-block-line--add" style="counter-set: icen-cb-dl-n 2">+const version = &quot;0.11.0&quot;;</span><span class="icen-code-block-line icen-code-block-line--add" style="counter-set: icen-cb-dl-n 3">+const codeBlock = true;</span><span class="icen-code-block-line icen-code-block-line--ctx" style="counter-set: icen-cb-dl-n 4"> </span><span class="icen-code-block-line icen-code-block-line--ctx" style="counter-set: icen-cb-dl-n 5"> export function boot() {</span><span class="icen-code-block-line icen-code-block-line--ctx" style="counter-set: icen-cb-dl-n 6">   return h(&quot;div&quot;, &quot;&quot;, version);</span><span class="icen-code-block-line icen-code-block-line--ctx" style="counter-set: icen-cb-dl-n 7"> }</span></code></pre></figure>
行为与接口说明

适合源码、终端记录和 diff。工具栏在悬停或键盘进入时显现,触屏始终可用;代码区域可聚焦并横向滚动,行号、换行和折叠同步读屏状态。复制保留空行,等待时避免重复操作,失败提供重试。浏览器用 renderCodeBlock,构建期用 codeBlockHTML;两者共用 initCodeBlock 委托与销毁。code 是安全文本入口,html 仅接收可信高亮结果,调用方负责其语法配色对比度;本站高亮已适配六套主题的明暗底色。下方展示双主题 TypeScript、长终端记录和增删差异。

安装

bun add @icen.ai/ui

一行引入该组件(CSS 与 behavior 自动带上,无需关心内部 CSS 文件名):

js
import { initCodeBlock } from '@icen.ai/ui/kit/code-block';initCodeBlock();

也可以用脚手架直接打印引入行:

bash
bunx --bun @icen.ai/ui add code-block

Astro 项目注意:kit 入口里的 CSS import 在页面 <script> 里会被 Astro 的 client bundle 摇掉——请改在布局 frontmatter 里引 CSS(import '@icen.ai/ui/components/code-block.css';),JS 行为仍可走 kit。Vite SPA / webpack 项目无此问题。

用法

js
import { renderCodeBlock, codeBlockHTML, initCodeBlock } from '@icen.ai/ui/behaviors/code-block';// 浏览器端(DOM):code 纯文本逐行 textContent(安全路径);html 为可信高亮槽位renderCodeBlock(el, {  code: 'const a = 1;',      // 或 html: shikiOutput  lang: 'ts',                // diff/patch 进 diff 模式(默认显示行号)  file: 'src/app.ts',        // 文件名徽标(优先于 lang 徽标)  title: '入口', lines: [1], // 说明标题 + 高亮行(1 基)  num: true, wrap: false,    // 初始行号 / 换行  fold: 20,                  // true 强制折叠 / false 永不 / 数字 = 超过 N 行折叠(缺省 30)});const dispose = initCodeBlock(el); // 可限定容器;重复初始化返回同一销毁函数// 宿主卸载前调用 dispose():回收监听、折叠动画与尚未完成的复制反馈// SSR/构建期(Astro/JSX):shiki 双主题高亮 + codeBlockHTML 直出整串标记(文档站 CodeBlock 即此方案)const html = codeBlockHTML({ html: shikiHighlighted, lang: 'ts', file: 'src/app.ts', num: true });// → <figure class="icen-code-block">…,可信输出用 set:html 注入// 页面初始化时 initCodeBlock();之后插入的代码块也会自动接入// 切换派 icen:code-block-toggle { el, action: 'num'|'wrap'|'fold', value }// 复制派 icen:copy-success { text } / icen:copy-error { text, reason }<!-- 或手写标记:figure.icen-code-block 契约见 code-block.css 头注释 -->