今天本来要写Latex的,结果意外发现网页端现在能用行间公式 \[\]包裹得到HTML的渲染。

想到是不是可以让Deepseek 也输出类似 Anthropic 这种网页端小卡片工具的效果。

像这样:

Anthropic 的 Artifact

DeepSeek 效果

感觉这个特性挺好玩的,以后就可以靠这个让它生成点小工具。也不用复制到html文件里面再打开。

特性研究

但是Deepseek跟Antropic 的这个Artifacts还不一样。

Deepseek是借助Mathjax渲染机制把html格式渲染的,但是mathjax本身渲染机制不支持<script>标签注入。所以直接用Anthropic 的方法,肯定是不行的。

这里用了 oninput="" onclick="" 事件属性 注入JavaScript

其他的一些事件属性也可以,如下表。

实现方式原理优点缺点
纯伪协议注入每次点击链接时运行一段逻辑(如 javascript:...)。极其简单,不需要修改网页源码,即点即用。无法实现实时“倒计时”效果,只能点击瞬间触发,属于被动交互。
定时器注入 (setInterval)注入一段 JS 脚本,在页面 DOM 中创建一个浮动层或修改标题。可以实现动态倒计时,实时显示北京时间或冷却进度,视觉效果拉满。逻辑较复杂,涉及 DOM 操作及定时器的内存管理(需要手动清除)。

href 接 JavaScript 伪协议也可以。

示例

<a href="javascript:(function(){window.t=window.t||0;var n=Date.now();if(n-window.t>3000){window.t=n;console.log('注入成功!北京时间:'+new Date().toLocaleString());}else{console.warn('冷却中...');}void(0);})();">
点我 Log 时间
</a>

但是这里有一个细节。如果你里面要声明变量,声明的变量一定不要用 let ,不然反复点击注入要 触发标识符重复定义的 Syntax Error

即

Identifier 'example' has already been declared

ok,那我们现在都搞清楚了,来构建提示词。

也可以构建Skill,像这样,我感觉效果还不错。

提示词

<提示词隔离区 开始>
# 你的技能Skill

## 🛠️ Skill: HTML卡片工具生成器

**Skill 名称:** generate_html_card

**触发时机:** 当用户需要可视化工具/转换工具/交互卡片时使用

**Skill 细节:**

生成卡片工具时严格遵守以下规则:

【卡片输出规则】
1. 卡片必须以 \\[ 开头,以 \\] 结尾(反斜杠+方括号)
2. \\[ 和 \\] 之间只放HTML代码,禁止用```html包裹
3. 所有CSS样式内联写在 style="" 里,每个规则独立换行
4. JS逻辑写在 onclick/oninput/onchange 等事件属性里,禁止使用<script>标签
5. 事件属性内变量声明用 var 或直接赋值,禁止使用 let/const
6. 禁止使用 Canvas API 绘制复杂图形,优先用 CSS 实现视觉效果(渐变、阴影、动画等)
7. 如果功能复杂,优先思考能否用 CSS 动画 + 简单 JS 触发器实现,而非编写大量 JS 逻辑
8. 必须保证卡片能正常交互,不能因为规则限制而输出无法运行的代码。你一定可以做到。

【对话交流规则】
1. 输出卡片后,你可以正常用文字向我解释工具的功能、设计、用法
2. 卡片工具和对话交流是分开的,互不干扰
3. 不要因为输出卡片就变成只会输出代码的机器
【反例警告与正确示范】
❌ 绝对禁止的输出形式:
<div>...</div>
<script>...</script>

✅ 唯一正确的输出形式(复杂逻辑请严格套用此模板):
\[
<div id="app">
  <!-- 界面元素 -->
  <button onclick="window.appAction && window.appAction()">点击</button>
  
  <!-- 隐藏初始化器:利用 onmouseover 一次性注入全部逻辑 -->
  <div style="display:none;" onmouseover="
    if (window._inited) return;
    window._inited = true;
    
    // 1. 定义数据
    var data = [];
    
    // 2. 定义工具函数挂载到 window
    window.parseMenu = function(text) { ... };
    window.saveData = function() { ... };
    
    // 3. 定义交互入口
    window.appAction = function() { ... };
    
    // 4. 初始化执行
    window.loadData();
  "></div>
</div>
\]

请务必模仿上述结构:使用隐藏 div 的 onmouseover 作为初始化脚本块,使用 window 对象挂载跨函数变量。禁止使用 <script> 标签。
</提示词隔离区 结束>

封装后的效果:

啊哈哈哈。实现了。

就是目前这一版提示词它输出的还是会带有卡片背景。

如果想要Artifacts那样透明背景 轻量的工具,可以直接试试这个提示词。

注意要适配localstorage 的主题(light dark 或system)

进阶提示词

<提示词隔离区 开始>
# 你的技能Skill

## 🛠️ Skill: HTML卡片工具生成器

**Skill 名称:** generate_html_card

**触发时机:** 当用户需要可视化工具/计算器/交互卡片时使用

---

## 📖 环境原理说明

你输出的HTML卡片运行在特殊渲染容器中,这导致:

1. **<style> 会失效**:被当作文本丢弃 → CSS必须内联在 style="" 里
2. **<script> 会失效**:被当作文本丢弃 → JS必须写在 onclick/oninput 等事件属性里
3. **let/const 重复声明报错**:事件属性每次触发在同一全局作用域 → 用 var 或直接赋值
4. **异步API丢失上下文**:setTimeout/setInterval 回调在全局执行,访问不到闭包变量 → 只用同步交互
5. **状态靠DOM属性**:数据存在 data-* 属性里,通过 getAttribute/setAttribute 读写
6. **主题自适应**:检测 localStorage 中的 __appKit_@deepseek/chat_themePreference 判断亮暗模式

---

## ✅ 正确示例(请模仿此结构)

\[
<div id="card" style="
  max-width: 360px;
  margin: 2rem auto;
  padding: 2rem 1.5rem;
  background: transparent;
  border-radius: 28px;
  box-shadow: 0 20px 35px -8px rgba(0,0,0,0.15);
  font-family: system-ui, 'Segoe UI', Roboto, sans-serif;
  border: 1px solid rgba(128,128,128,0.2);
  transition: all 0.2s;
">
  <div style="
    margin-bottom: 1.5rem;
    font-weight: 650;
    font-size: 1.5rem;
    border-bottom: 2px solid rgba(128,128,128,0.2);
    padding-bottom: 0.8rem;
  " id="title">
    🔢 奇偶判定
  </div>
  
  <div style="margin-bottom: 1.5rem;">
    <label style="
      display: block;
      font-size: 0.8rem;
      font-weight: 600;
      margin-bottom: 6px;
      text-transform: uppercase;
    " id="label">输入整数</label>
    <input type="number" id="numInput" placeholder="例如 42" value="" style="
      width: 100%;
      padding: 14px 16px;
      font-size: 1.2rem;
      border: 2px solid rgba(128,128,128,0.3);
      border-radius: 18px;
      background: transparent;
      outline: none;
      box-sizing: border-box;
    " oninput="
      var val = this.value.trim();
      var resDiv = document.getElementById('resultDisplay');
      var iconSpan = document.getElementById('resultIcon');
      var textSpan = document.getElementById('resultText');
      if (val === '') {
        resDiv.style.background = 'rgba(128,128,128,0.1)';
        iconSpan.innerHTML = '🔍';
        textSpan.innerText = '等待输入';
        return;
      }
      var num = Number(val);
      if (isNaN(num) || !Number.isInteger(num)) {
        resDiv.style.background = 'rgba(200,70,50,0.15)';
        iconSpan.innerHTML = '⚠️';
        textSpan.innerText = '需要整数';
        return;
      }
      var isEven = num % 2 === 0;
      resDiv.style.background = isEven ? 'rgba(40,160,80,0.15)' : 'rgba(220,120,40,0.15)';
      iconSpan.innerHTML = isEven ? '⭕' : '🔘';
      textSpan.innerText = isEven ? '偶数' : '奇数';
    ">
  </div>
  
  <div id="resultDisplay" style="
    background: rgba(128,128,128,0.1);
    border-radius: 20px;
    padding: 1.2rem 1.5rem;
    display: flex;
    align-items: center;
    gap: 16px;
    transition: background 0.2s;
  ">
    <span id="resultIcon" style="font-size: 2.5rem;">🔍</span>
    <span id="resultText" style="font-size: 2rem; font-weight: 650;">等待输入</span>
  </div>
  
  <div style="
    margin-top: 1.2rem;
    font-size: 0.75rem;
    text-align: right;
    opacity: 0.6;
  ">
    ⚡ 实时判定
  </div>
</div>

<!-- 主题自适应 + 初始化 -->
<div style="
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 9999;
" onmouseover="
  if (window._themeInited) return;
  window._themeInited = true;
  
  var themeJson = localStorage.getItem('__appKit_@deepseek/chat_themePreference');
  var isDark = false;
  if (themeJson) {
    try {
      var themeData = JSON.parse(themeJson);
      if (themeData.value === 'dark') isDark = true;
      else if (themeData.value === 'system') isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    } catch(e) {}
  }
  
  var textColor = isDark ? '#f0f0f0' : '#1a1a1a';
  var card = document.getElementById('card');
  var title = document.getElementById('title');
  var label = document.getElementById('label');
  var input = document.getElementById('numInput');
  var resultText = document.getElementById('resultText');
  
  if (card) card.style.color = textColor;
  if (title) title.style.color = textColor;
  if (label) label.style.color = isDark ? '#b0b0b0' : '#5f5f73';
  if (input) {
    input.style.color = textColor;
    input.style.borderColor = isDark ? 'rgba(255,255,255,0.2)' : 'rgba(0,0,0,0.2)';
  }
  if (resultText) resultText.style.color = textColor;
  
  var self = document.getElementById('themeTrigger');
  if (self) self.style.display = 'none';
" id="themeTrigger"></div>
\]

---

## 🛠️ 输出规则

1. 必须以 \\[ 开头,以 \\] 结尾
2. 中间只放HTML,禁止用```html包裹
3. CSS内联写在 style="" 里,每个规则独立换行
4. 背景用 transparent,文字颜色通过主题检测动态设置
5. JS写在 onclick/oninput 等事件属性里,禁止 <script>
6. 变量用 var 或直接赋值,禁止 let/const
7. 禁止 setTimeout/setInterval/requestAnimationFrame
8. 交互设计为同步模式:触发→计算→更新DOM→结束

---

## 💬 对话规则

输出卡片后,用文字解释功能、设计思路。卡片和对话互不干扰。

</提示词隔离区 结束>

效果相当好哇。


哈哈哈,感觉很有意思。相当于用 Deepseek 也拥有Claude Artifacts 的技能了。
如果喜欢的话,点个赞呀~


收录于知乎专栏「js逆向实例」· 查看知乎原文