╔══════════════════════════════════════════════════════╗
║              NUD 语言语法库 (v1.0)                  ║
║         New User's Dream — 新手编程的梦              ║
╚══════════════════════════════════════════════════════╝

当前版本：v1.0 ｜ 程序里可用 print(str ver) 查看版本
本文档中 ← 在排版说明里用于指向输出示例。但在代码里 ← 也是行内注释起始符
（如 print(str a) ← 输出: 1,2），从 ← 开始到行末会被忽略。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
一、程序入口
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

/being/
程序第一行必须写，没有则直接报错。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
二、变量
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

【赋值】
  数字直接写：a=11    字符串用双引号：b="文字"    变量复制：c=a
  算术运算：a=a+1（自增）  a=a-b（减变量）  右边可以是数字或变量

【类型】
  整数：a=11    小数：a=3.14    字符串：b="文字"

【命名规则】（公告 v0.9）
  - 变量名/按钮名/函数名/参数名统一规则：只能由字母、数字、下划线组成，且只能字母开头
  - 正确：a  myvar  num1  x2  my_var    错误：1a  _test  分  my-var
  - 不能用中文（会报错并提示"名字不能用中文"）
  - 变量名不能为关键词（print/if/for/def/import/wait/input/open/key 等）
  - x、y、xy 是坐标保留字，不能当变量名；读按钮坐标用 x(按钮名) / y(按钮名)
  - 变量名和按钮名共用命名空间，不能重名（解释器运行前检查，报错并提示定义行号）
  - 把变量当按钮文字显示不算冲突：/bot/shown/txt/str n/  ← 正确

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
三、输出
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

print("内容")              ← 打印字符串
print(变量名)               ← 打印字符串变量
print(str 变量名)           ← 打印数字变量（必须先 str 转换）
print("内容")c=色号         ← 彩色文字（色号30-37，见第八节）
print(str 变量名)c=色号     ← 彩色数字

wait(n)                    ← 暂停 n 秒（支持整数和小数，如 wait(1) / wait(0.2)），主程序和事件块里均可

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
四、条件判断
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

if 条件:
    ...
else:
    ...

比较符：= 等于  != 不等于
else 可省略。条件成立执行 if 块，否则执行 else 块。

【比较运算（v0.7，需 import nud-if）】
import nud-if 后，if 条件支持大小比较和逻辑组合：

  比较：> 大于   < 小于   >= 大于等于   <= 小于等于
  逻辑：or 或    and 和（逻辑与，非拼接）

例：
  import nud-if
  score=85
  if score>80:
      print("优秀")c=32
  if score<60:
      print("不及格")c=31
  if score=100 or score=99:
      print("满分附近")c=33
  if score>60 and score<90:
      print("中等")c=36

说明：
  - 比较和逻辑组合必须 import nud-if 后才可用；不导入仍报错
  - and 在 if 里是"逻辑和"，和拼接 and（print("a"andb)）互不冲突

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
五、循环
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

【计次循环】
  for(次数):
      ...
  次数可写数字或变量。括号必填，冒号可省略。for (10) 也可（括号前可空格）。
  例：for(10): → 循环10次    n=3  for(n): → 循环3次

【条件循环】
  for 条件:
      ...
  反复执行直到条件成立时停止。比较符同 if。
  内置保护：最多循环 10000 次后自动跳出。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
六、按钮与物品
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

【按钮 /bot/】
  /bot/名字/txt/内容[/c=色号][/x=坐标X][/y=坐标Y][/img="图片路径"]

  txt 内容支持三种写法："引号文字"  str 变量  变量
  也可以不写文字：/bot/名字/txt//x=0/y=0 → 无文字纯色块按钮（配合 c= 色号或 img= 图片）
  c/x/y/img 全部可选，顺序任意，缺省用默认值。

  属性说明：
    名字  按钮名（字母开头，ASCII 字母数字，禁用下划线）
    txt   显示内容
    c     底色 30-37（缺省32绿色），文字固定白色
    x,y   Scratch 坐标（-240~240 / -180~180），超出自动 clamp，缺省 0,0
    img   贴图路径（双引号，相对路径），缺失时回退为文字按钮

  点击事件：按钮下一行起缩进4格写代码，即点击事件块（同 if/for 缩进规则）。
  不写缩进块 = 静态按钮，点了没反应。

  例：
    /bot/startbtn/txt/"开始"/x=0/y=80
        print("你点击了开始")c=32
    /bot/aboutbtn/txt/"关于"/x=120/y=-60

【物品 /p/】
  语法同 /bot/，把 /bot/ 换成 /p/。区别：
  - 不能点击（无点击事件）
  - 可以移动和滑动（/m/ /sm/ 均可作用）
  - 可被 /h/ 隐藏、/L/ 显示

  例：
    /p/apple/txt/"苹果"/x=-100/y=0
    /bot/eat/txt/"吃掉"/x=0/y=80
        /h/apple/h/        ← 点按钮，苹果消失

【隐藏 /h/】
  /h/名字/h/
  把按钮或物品从舞台藏起来。必须写在声明之后（主程序里），事件块里可隐藏后面声明的。
  名字必须是已声明的，找不到会报错。隐藏不是删除，可用 /L/ 恢复。

【显示 /L/】
  /L/名字/L/
  把被隐藏的重新显示。L 必须大写。对已显示的执行无副作用。

【计数器示例】
  n=0
  /bot/jishu/txt/"计数+1"/c=32/x=120/y=60
      n=n+1
      print(str n)
  /bot/shown/txt/str n/c=34/x=-120/y=-80

【旧版兼容】
  按钮 名称 "文字" x=N y=M [图="路径"]   ← 旧写法仍可用，x/y 必填，建议用 /bot/

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
六点五、询问输入（v0.7）
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

弹输入框让用户输入，存到变量：

  英文变量名 = input("提示文字")

例：
  /being/
  name = input("你的名字")
  print("你好，"and name and"！")c=36

说明：
  - 程序跑到这行会弹出输入框，输入的内容存进变量
  - IDE 弹输入框；命令行（CLI）在终端输入
  - 按钮点击事件里也能用
  - 输入的是字符串，但和数字比较/判断时会自动当数字处理（如 if score>60）

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
六点六、键盘检测（v0.7，需 import nud-key）
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

开启检测后，持续收集所有按下的键，存起来、无间隔拼接：

  import nud-key      开启键盘检测能力
  open key            开启检测（一直检测，直到 die key）
  a=<key>             把检测期间按下的所有键（无间隔拼接）存到 a，然后清空
  die key             强制关闭检测（可配合 if 用）

例（竖着写，用 # 分隔）：
  import nud-key
  open key#wait(1)    开启检测，等 1 秒（期间按键被收集）
  a=<key>             取出这 1 秒按下的键
  print("你按了:"and a)c=32
  die key

或配合按钮：
  /bot/测/txt/"测键盘"/x=0/y=0
      open key#wait(1)
      a=<key>
      print("你按了:"and a)c=32
      die key

说明：
  - open key 是"一直检测"；不用写 if key(...)，开启后按键直接进 <key>
  - 编辑器里输入 key( 会弹出可选按键列表，点击即可补全
  - die key 可配合 if 条件判断要不要关

【小游戏示例：键盘控制小球吃★】（完整源码在 项目/键盘小游戏.und）
  /being/
  ^WASD 控制 ● 去吃 ★，集满 5 分赢！^
  import nud-key
  import move
  import xym
  import nud-if
  score=0
  gx=200
  gy=150
  /bot/ball/txt/"●"/x=0/y=0
  /bot/gold/txt/"★"/x=200/y=150
  print("WASD 控制 ● 去吃 ★！集满 5 分就赢！")c=33
  for score>=5:
      open key#wait(0.5)
      k=<key>
      if k="w":
          /xym/ball/y-10
      if k="s":
          /xym/ball/y+10
      if k="a":
          /xym/ball/x-10
      if k="d":
          /xym/ball/x+10
      if x(ball)=gx:
          if y(ball)=gy:
              score=score+1
              print("得分！现在 "and score)c=32
              if score=1:
                  gx=-200
                  gy=150
              if score=2:
                  gx=150
                  gy=-150
              if score=3:
                  gx=-150
                  gy=100
              if score=4:
                  gx=50
                  gy=-100
              /m/gold/x=gx/y=gy
  print("🎉 你赢了！集满 5 分！")c=33

  要点：
  - 每个按钮/物品有自己的坐标，用 x(按钮名) / y(按钮名) 读取（x/y 是坐标保留字，不能当变量）
  - 相对移动用 /xym/按钮名/x+10 或 /xym/按钮名/y-10（y+ 向下、y- 向上，屏幕直觉）
  - for 条件循环是"条件成立就停"；wait 支持小数秒

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
七、移动
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

【瞬移】
  /m/名字/x=N/y=M    ← 瞬间传送到 (N,M)，超出自动 clamp
  /m/名字/x=x/y=y    ← 坐标可以是变量（v0.9，做动态移动/小游戏）

【滑动】
  import slowmove（或 import sm）
  /sm/名字/x=N/y=M   ← 平滑滑动到目标位置

  滑动后紧跟 wait(秒数) 控制动画时长（wait 被消费，不再额外暂停）：
    /sm/名字/x=N/y=M
    wait(2)      ← 2秒滑过去
  不写 wait 默认约0.3秒。wait(0) 表示瞬间到位。
  仅对事件块内的滑动有动画效果（按钮已在舞台上）。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
八、导入与色号
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

import sm        ← 启用 sm: 滑动语法
import slowmove  ← 启用 /sm/ 滑动语法（等价）
import botlight  ← 启用按钮高亮
import bm        ← 启用随机数
import pop       ← 启用弹窗

【按钮高亮 botlight】
  import botlight 后默认开启高亮：
  botlight_0  ← 关闭    botlight_1  ← 开启
  写在按钮声明前全局生效，也可写进事件块运行时切换。

【色号对照表】
  30黑  31红  32绿  33黄  34蓝  35紫  36青  37白
  用于 print 文字颜色和 /bot/ 按钮底色。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
九、块结构与缩进
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

规则与 Python 一致：if/else/for//bot/ 下子语句缩进4格，同层对齐，取消缩进代表块结束。

【注释】
  ^注释内容^    ← 用 ^ 包裹，支持整行注释和行尾注释
  # 开头的行仍支持。旧的 // 写法已停用，请改用 ^注释^。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
十、自定义函数
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

/def/(函数名)/v/参数1/v/参数2#语句1#语句2

函数名写在括号里；参数用 /v/参数名/ 声明；函数体用 # 分隔（同一行内）。
函数体支持 print/wait/移动/隐藏/赋值/嵌套调用其他函数，也支持 if/else 和 for 控制流。

函数体内的控制流（v0.9）：
  if 条件:#语句1#语句2#else#语句3     ← if/else 用 # 分隔，else 归属最近的 if
  for(次数):#语句1#语句2             ← 计次循环
  for 条件:#语句                     ← 条件循环

例：
  /def/(check)/v/n#if n=1:#print("一")#else#print("不是一")
  def check /n=1      ← 输出：一
  /def/(loop)#for(3):#print("圈")
  def loop            ← 输出：圈 圈 圈

调用：
  def 函数名
  def 函数名 /参数名=值/参数名=值

例：
  /def/(greet)/v/name#print("你好，")#print(str name)
  /bot/b/txt/"打招呼"/x=0/y=0
      def greet /name="老板"

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
十一、触碰事件（v0.7）
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

谁碰到目标（按钮/物品），就执行 # 后面的语句。不能缩进，只能用 # 标记。

  /bot/车/txt/"车"/x=0/y=0
  /p/墙/txt/"墙"/x=100/y=0
  /e/墙/e/#print("我撞车了")c=31

说明：
  - /e/名字/e/ 写在目标（按钮/物品）声明之后
  - # 后是触碰时执行的语句（只用 #，不用缩进）
  - 没写 # → 不报错，触碰时啥也不做
  - 找不到目标报「找不到名为 x 的东西」
  - 触碰判定：坐标距离 ≤ 50 算碰到

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
十二、坐标移动 + 读坐标（v0.7，需 import xym）
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

xym 是引擎内置库（代码直接内置在引擎里，不需要 lib/ 文件夹里的文件），
import xym 才启用下面这些语法；不 import 时 /xym/ 和 x()/y() 都会提示先导入。

把按钮/物品在当前位置上相对移动（屏幕直觉方向）：

  import xym
  /xym/名字/x+100     ← 向右移 100
  /xym/名字/x-100     ← 向左移 100
  /xym/名字/y+100     ← 向下移 100
  /xym/名字/y-100     ← 向上移 100

读取按钮当前坐标（配合 if 判断用）：

  if x(按钮名)=120:    ← x(按钮) 读横坐标
  if y(按钮名)=-80:    ← y(按钮) 读纵坐标

说明：
  - x/y 后跟 +/- 数字，多次写会累加
  - 坐标会自动限制在舞台范围（x:-240~240, y:-180~180）
  - x/y/xy 是坐标保留字，不能当变量名（公告规则）；读坐标用 x(按钮)/y(按钮)
  - 没写 import xym 会提示

例：
  import xym
  /bot/车/txt/"车"/x=0/y=0
  /xym/车/x+100

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
十三、拼接（v0.7）
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

用 and 把文字和变量拼成一个字符串，可以多个连拼：

  print("你好，"and name and"！")
  新字符串 = 字and变量and字

说明：
  - 每段可以是带引号的文字 "..." 或变量名
  - 变量会自动转成字符串（数字也能拼）
  - 未定义的变量会报错

例：
  name="老板"
  print("你好，"and name and"！")c=36    ← 输出：你好，老板！
  gift="宝石"
  c = "你有"and gift and"吗"
  print(str c)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
十四、列表（v0.7）
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

一个变量存多个值，用方括号 [ ]，元素之间用逗号：

  a=[1,2]          ← 定义列表
  print(str a)     ← 输出整个列表：1,2（逗号连接，无空格）
  print(int 0 a)   ← 取第 1 个元素（下标从 0 开始）：1
  print(int 1 a)   ← 取第 2 个元素：2

说明：
  - 元素可以是数字、带引号的文字 "..."，也可以是已定义的变量名
  - 下标从 0 开始，超出范围会报错（列表 'a' 没有第 N 个元素）
  - 支持带颜色：print(int 0 a)c=32 / print(str a)c=36
  - 按钮点击事件里也能用

例：
  a=[1,2]
  print(str a)     ← 1,2
  print(int 0 a)   ← 1
  print(int 1 a)   ← 2
  b=["苹果","香蕉"]
  print(str b)     ← 苹果,香蕉

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
十五、音乐（v0.8，只支持 MP3）
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

播放 MP3 音乐，支持音量、倍速、暂停：

  /m/"音乐.mp3"/m/     ← 播放（文件放 .und 同目录或 NUD 目录）
  /m/v=100/m/          ← 音量 0-100（100 最大声）
  /m/s=1/m/            ← 倍速（1 正常，2 两倍速，0.5 半速）
  /m/die/m/            ← 暂停

说明：
  - 只支持 MP3 格式
  - 音量范围 0-100，默认 100
  - 倍速 1 为正常速度，可小数（0.5 = 半速）
  - /m/die/m/ 是暂停（不是停止），再次 /m/"歌.mp3"/m/ 重新播放
  - 找不到文件会提示放哪
  - 按钮点击事件里也能用

例：
  /m/"bgm.mp3"/m/
  /m/v=80/m/
  wait(5)
  /m/die/m/

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
十六、随机数（v0.7，需 import bm）
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

import bm
英文变量名 = bm/最小/最大    ← 生成最小~最大随机整数（两端含）

最小/最大 可以是数字或变量。最小比最大大会自动交换。

例：
  import bm
  x = bm/1/100
  print(str x)c=32

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
十七、弹窗（v0.6，需 import pop）
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

{pop}...{pop} 弹窗块，内部用标记声明元素，支持一行或多行写法。

  import pop
  {pop}<ti>标题<ti><txt>内容<txt><bot>按钮<bot><cil>点击显示<cil>/h=300/a=240{pop}

  或多行：
  {pop}
  <ti>标题<ti>
  <txt>内容<txt>
  <bot>按钮<bot>
  /h=300/a=240
  {pop}

【标记】
  <ti>标题<ti>          弹窗标题
  <txt>正文<txt>        正文第一行
  <txt2>第二行<txt2>    第二行（颜色较浅）
  <bot>按钮<bot>        按钮（可多个）
  <cil>点击显示<cil>    点按钮后显示的内容
  <h>隐藏字<L>显示字<b>  双态控件（点击切换）
  /h=高/a=宽            弹窗尺寸（像素），默认 300x240
  最大N                 最多 N 个元素（默认 9）

【开关】
  弹窗打开后一直保持，直到写 <pop>die<pop> 关闭最近弹窗。
  没写 die 弹窗不关（可手动点 X 关）。

例：
  import pop
  {pop}<ti>我的弹窗<ti><txt>欢迎使用NUD！<txt><bot>确定<bot><cil>你点了确定<cil>{pop}
  print("弹窗开着，程序照常跑")c=36
  <pop>die<pop>
  print("弹窗已关闭")c=32

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
十八、自定义库（v0.8）
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

可以自己用 Python 写库！把 .py 文件放进 NUD 的 lib/ 目录，
NUD 里写 import 库名 就能用——不限于内置库。

写库三步：

第 1 步：在 lib/ 目录建一个 Python 文件，如 lib/emoji.py
第 2 步：写两个函数（库的接口）：

  parse_line(content, line_num, sm_imported)  ← 识别语法
    - 认识这行 → 返回 ({'type': '我的类型', 'line': line_num, ...参数}, None)
    - 认识但写错 → 返回 (None, '错误说明')
    - 不认识 → 返回 (None, None)

  execute(node, env, registry, moves, output)  ← 执行
    - node 是解析出的节点（node.type / node.参数 取数据）
    - output 是输出列表，output.append((文字, 色号)) 打印
    - registry 是所有按钮/物品，moves 是移动命令列表

第 3 步：NUD 代码里用：

  /being/
  import emoji
  /emoji/开心     ← 输出 😊

例（lib/emoji.py 的简化版）：

  import re
  EMOJI = {'开心': '😊', '生气': '😠'}
  _RE = re.compile(r'^/emoji/([一-龥A-Za-z_][一-龥A-Za-z0-9_]*)$')

  def parse_line(content, line_num, sm_imported):
      m = _RE.match(content)
      if not m:
          return None, None
      if m.group(1) not in EMOJI:
          return None, f'没有这个表情: {m.group(1)}'
      return {'type': 'EMOJI_PRINT', 'line': line_num, 'name': m.group(1)}, None

  def execute(node, env, registry, moves, output=None):
      name = getattr(node, 'name', '')
      if output is not None:
          output.append((EMOJI.get(name, ''), None))
      return None

说明：
  - import 找不到 lib/库名.py 会报「找不到语法库」
  - 库写错语法时返回 (None, 错误)，引擎会把错误提示给用户
  - 内置的 move 库就是现成范例：NUD0.9/lib/move.py
  - 顶层、按钮事件、函数体里都能用自定义库

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
十九、完整示例
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

/being/
a=11
b="nud 语言"
print(b)
print("——")
print(str a)

if a=10:
    print("a 等于 10")
else:
    print("a 不等于 10")

for(3):
    print("第次")

n=0
/bot/startbtn/txt/"开始"/x=0/y=80
    print("你点击了开始")c=32
    n=n+1
/bot/aboutbtn/txt/"关于"/x=120/y=-60

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
附：积分系统（v0.6，仅在线版）
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

积分制仅存在于官网在线编辑器，本地 IDE 免费无限用：
  - 默认 100 积分，运行一行扣 1 分
  - 耗尽后等待 1~10 分钟（随机）自动恢复
  - 恢复期间运行提示「NUD 正在恢复，错误编号：012」
  - 记录存在浏览器 localStorage
