rCodenow 移动模式插件
  • TypeScript 70.4%
  • CSS 21.5%
  • JavaScript 8.1%
Find a file
2026-10-23 18:05:38 +08:00
docs 注释整理&LICENSE设置 2026-10-02 23:35:23 +08:00
scripts 注释整理&LICENSE设置 2026-10-02 23:35:23 +08:00
src 高级自制积木与模组支持 2026-10-23 18:05:38 +08:00
test 注释整理&LICENSE设置 2026-10-02 23:35:23 +08:00
.gitignore 初次上传 2026-09-30 23:12:36 +08:00
LICENSE 注释整理&LICENSE设置 2026-10-02 23:35:23 +08:00
package-lock.json 注释整理&LICENSE设置 2026-10-02 23:35:23 +08:00
package.json 高级自制积木与模组支持 2026-10-23 18:05:38 +08:00
README.md 注释整理&LICENSE设置 2026-10-02 23:35:23 +08:00
tsconfig.json 初次上传 2026-09-30 23:12:36 +08:00
VERSION.md 高级自制积木与模组支持 2026-10-23 18:05:38 +08:00
vite.config.ts 初次上传 2026-09-30 23:12:36 +08:00

MobileMode(移动模式)

rCodenow 的移动模式插件:启用即接管 GUI 布局,把编辑器换成触控优先的 单屏工作区 + 左侧浮动侧坞 + 右侧吸附工具箱,横竖屏通用。

插件不检测宿主平台 —— 桌面端、浏览器、移动端行为完全一致,启用即生效。 横竖屏差异全部由 CSS 处理,而不是由 JS 判断要不要接管布局;桌面端装上它同样 得到移动布局,在桌面上验证布局问题反而更方便。

  • 包名:rcodenow-mobile-mode
  • 插件 id:mobilemode(meta.namespace = https://rc.0832.ink/Gallery/extensions/mobilemode)
  • 产物:dist/rcodenow-mobile-mode.min.js(单文件 ES module,无外部依赖)
  • 许可:GPL-3.0

特性

区域 行为
顶栏 移出屏幕(translateY(-101%),不是 display:none——后者会废掉二级菜单),导航改由侧坞承载
侧坞 左侧浮动 icon 侧坞(不挤压工作区),可拖拽定位并记忆;镜像顶栏「设置 / 文件 / 编辑 / 插件 / 高级」菜单
副屏 代码 / 造型 / 背景 / 声音 / 舞台 五屏独立,一屏一件事;舞台屏只留 canvas
工作区 标签栏隐藏,编辑器占满全宽全高;工具箱吸附到右侧(RTL 自动镜像)
积木区 自动隐藏:轻点工作区收起、从积木区取出积木后自动收起;展开时覆盖在工作区上,不改变工作区宽度
造型 / 背景 / 声音 素材列表改为贴底横滑条(column-reverse),编辑器占主体
库模态 资源库 / 扩展库 / 插件库等全屏模态贴边铺满、双列、加大触摸目标
积木引擎 同时兼容 scratch-blocks 与 rKittenBlink(两条独立路径)

侧坞展开态自上而下:拖拽手柄 · 收回 · 设置 · 文件 · 编辑 · 插件 · 高级 · (弹性间隔)· 添加角色 · 更多 · 舞台 · 角色缩略图列表 · 启动 / 暂停 / 终止。 完整按钮表见 docs/sidebar.md。


快速开始

构建

npm install
npm run build   # clean + tsc + vite build + postbuild 校验 → dist/
npm test        # 回归测试(需先 build)
npm run check   # lint + typecheck + build + test

本地开发(热更新)

npm run dev                 # 默认 8141
npm run dev -- --port 9000  # 换端口
地址 用途
http://localhost:8141/dist/rcodenow-mobile-mode.min.js 插件 URL,填进 rCodenow 插件管理器的「自定义插件地址」
http://localhost:8141/__hmr SSE 端点,重建成功后通知页面 reload

改 src/ 会自动重建并推送 reload;dev server 只在响应时给 dist/*.js 追加 HMR 客户端,磁盘产物不受污染。

GUI 正式版把插件代码存进 IndexedDB,reload 会读到旧副本。自动更新需要宿主暴露 window.__RCODENOW_PLUGIN_DEV__.update 钩子,否则需在插件管理器里手动「更新」一次。

部署 / 安装

把 dist/rcodenow-mobile-mode.min.js 部署到与 meta.namespace 对应的地址 (https://rc.0832.ink/Gallery/extensions/mobilemode.js)。用 ?addon=<url> 加载时 会走宿主的 isTrustedRemotePluginUrl 校验:必须 HTTPS 且落在 src/addons/custom/plugin-security.js 的白名单前缀内。

rCodenow-Desktop 的 Android 端首次启动会自动带上该 ?addon= 参数;设置 RC_MOBILE_MODE_PLUGIN_URL= 可关闭。


目录结构

MobileMode/
├── src/
│   ├── plugin-entry.ts        # 元数据 + 入口:装配各模块、CSS 自注入、activate/deactivate
│   ├── mobile-activation.ts   # 启停调度(无平台检测)
│   ├── mobile-classes.ts      # body 类名集中定义 + 旧版 PortraitMode 残留清理
│   ├── mobile-layout.ts       # 布局硬约束兜底:内联 !important 写回 + 事件驱动纠偏
│   ├── mobile-screens.ts      # 副屏切换(workspace / costumes / sounds / stage / backdrops)
│   ├── mobile-sidebar.ts      # 浮动侧坞、弹出菜单面板、角色缩略图、拖拽定位
│   ├── mobile-menu.ts         # 顶栏二级菜单的「镜像」读取
│   ├── mobile-toolbox.ts      # 工具箱右侧吸附 + 积木区自动隐藏 + 分类栏自愈
│   ├── styles/mobile.css      # 全部布局样式
│   └── types/                 # plugin-api.d.ts / css.d.ts
├── scripts/
│   ├── dev.mjs                # 零依赖 dev server:静态服务 + 监听重建 + SSE 热更新
│   └── postbuild.mjs          # 校验产物是合法单文件 ESM
├── test/activation.test.mjs   # 回归测试:启用即开启,且从不读取 navigator
├── docs/                      # 详见下方「文档」
├── LICENSE                    # GPL-3.0 全文(FSF 原文)
├── vite.config.ts
└── tsconfig.json

工作原理

完整说明见 docs/ARCHITECTURE.md,这里只列骨架。

  1. 类名驱动:所有样式挂在 <body class="mobile-mode"> 下,由插件自行注入 <style id="mobile-mode-style">(宿主的 buildManifest 不会为自定义插件生成 userstyles 条目,故双保险:既导出 style 也自注入)。
  2. 副屏切换:workspace / costumes / sounds / backdrops 通过点击真实 Tab 元素 驱动编辑器(display:none 的标签栏依然会响应 React 合成事件);stage 只切 CSS。 ⚠ 不能改成手动 dispatch action——键名不匹配会让 activeTabIndex 变 undefined, react-tabs 直接崩。
  3. 顶栏菜单镜像:把原生下拉菜单读成数据(标签 / 图标 / 禁用态 / 选中态 + 从 React 内部 props 抓到的真实回调),由侧坞自己渲染面板。读不到就整体回退到 「唤出真实顶栏 + 点它的按钮」,绝不出现点了没反应的死按钮。
  4. 布局兜底:GUI 自带内联 min-width:1024 / flex-basis:596px,一旦样式表失效 就会顶开页面。除样式表外,还用内联 !important 把关键物理约束写进 DOM,并由 400ms 轮询 + MutationObserver / ResizeObserver(rAF 去抖)持续纠偏,停用时逐条撤销。
  5. 工具箱换侧与积木区:用两引擎共同支持的 toolboxPosition 原生机制换到右侧, 再按引擎分别处理积木区可见性(scratch-blocks 走 CSS margin;rKittenBlink 走引擎 原生 auto_close),并修补 getWidth() / getMetrics() / delete_area_margin 等几何假设。

类名契约(JS ↔ CSS 一一对应)

类名 含义
mobile-mode 布局总开关(body)
mobile-screen-<name> 副屏
mobile-sidebar-open 侧坞展开
mobile-menu-bar-visible 唤出真实顶栏(仅菜单镜像失败时的兜底路径)
mm-engine-kitten 引擎标记:命中 rKittenBlink 时打上,CSS 据此排除 scratch-blocks 专用规则

npm 脚本

脚本 说明
npm run dev dev server:静态服务 + 监听 src/ 自动重建 + SSE 热更新(默认 8141)
npm run build clean → tsc(dist/esm/ + 类型)→ vite build(单文件 bundle)→ postbuild 校验
npm run build:types 只出 .d.ts
npm run build:bundle 只跑 vite 打包
npm run typecheck tsc --noEmit
npm run lint ESLint(--max-warnings 0)
npm test 回归测试(需先 build)
npm run check lint + typecheck + build + test

eslint 未列入 devDependencies,仓库里也没有 eslint 配置 —— npm run lint / npm run check 需要先自行安装 eslint 并补配置,否则前两步会失败。 npm run build && npm test 与 npm run typecheck 无此依赖。


调试

手段 说明
window.__mobileModeLog 诊断日志环形缓冲(最近 200 条,{t, event, ...}),取证布局溢出 / 分类栏消失之类概率问题
URL ?mobile=0 强制关闭移动模式(并写入 localStorage['rc-mobile-mode']='0'),桌面端排障用;?mobile=1 强制打开
localStorage['mobile-dock-pos'] 侧坞位置记忆(JSON {x, y}),删掉即回到默认位
控制台 [MobileMode] 前缀 初始化、启停、tab 找不到等日志

除了 ?mobile=0 之外没有任何关闭路径 —— 要关就禁用插件本身。


注意事项(改代码前先读)

这些是踩过坑之后定下的硬约束,docs/ 里有完整根因分析:

  • 禁止用 CSS 改 flyout 位置——会破坏 Blockly 内部几何,导致积木无法拖动。换侧只能用 toolboxPosition(且 ws.toolboxPosition、toolbox.toolboxPosition、 ws.toolbox_position 三个都要设)。
  • 不要给 .blocklySvg / .blocklyMainBackground 写 CSS 宽高——scratch-blocks 的 SVG 没有 viewBox,内部坐标系依赖 width/height 属性(由 Blockly.svgResize 维护), CSS 100% 会让滚动条与 zoom 控件错位、底部露底。
  • 顶栏不能用 display:none——二级菜单读不到;用 translateY(-101%)。
  • 收起积木区的 margin 规则是 scratch-blocks 专用,都带 :not(.mm-engine-kitten); Kitten 需要的是隐藏态 visibility: hidden。
  • patch Blockly.inject 时必须把原函数的静态成员(含不可枚举的)搬到 wrapper 上, 否则「制作新的积木」会抛 bindDocumentEvents_ is not a function。
  • 退出时逐项摘补丁(不只依赖 active 标志),否则会残留到后续非移动模式会话。

与 hide-flyout 插件共存

移动模式独占积木区的收起几何:CSS 中和掉它的 .sa-flyout-placeHolder / .sa-flyout-border-* (否则会挡住工作区、只剩左边一小条可用),并持续压制它挂在 toolbox/flyout 上的 mouseenter / mouseleave(其方向逻辑基于「工具箱在左」)。开合只由「点分类 / 点工作区 / 锁定」驱动。


文档

文件 内容
docs/ARCHITECTURE.md 布局总览、模块职责、启停、类名契约、副屏切换、布局兜底、诊断日志
docs/sidebar.md 侧坞结构与按钮表、拖拽定位、角色缩略图、弹出菜单面板、顶栏菜单镜像
docs/toolbox.md 工具箱换侧、积木区自动隐藏、跨引擎命名对照、全部修过的问题
docs/styles.md 样式表硬约束(Blockly SVG 尺寸、CSS 变量、收起位移等)

源码里只保留「不能改」的硬约束注释,说明性内容一律放 docs/。


许可

GPL-3.0 —— 完整条款见 LICENSE。

Copyright (C) 2026 rCodenow

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

分发 dist/rcodenow-mobile-mode.min.js 时必须附带本许可证与对应源码的获取方式 (§4/§6);本插件的源码即本仓库的 src/。