- TypeScript 70.4%
- CSS 21.5%
- JavaScript 8.1%
| docs | ||
| scripts | ||
| src | ||
| test | ||
| .gitignore | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| VERSION.md | ||
| vite.config.ts | ||
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,这里只列骨架。
- 类名驱动:所有样式挂在
<body class="mobile-mode">下,由插件自行注入<style id="mobile-mode-style">(宿主的buildManifest不会为自定义插件生成 userstyles 条目,故双保险:既导出style也自注入)。 - 副屏切换:
workspace / costumes / sounds / backdrops通过点击真实 Tab 元素 驱动编辑器(display:none的标签栏依然会响应 React 合成事件);stage只切 CSS。 ⚠ 不能改成手动 dispatch action——键名不匹配会让activeTabIndex变undefined, react-tabs 直接崩。 - 顶栏菜单镜像:把原生下拉菜单读成数据(标签 / 图标 / 禁用态 / 选中态 + 从 React 内部 props 抓到的真实回调),由侧坞自己渲染面板。读不到就整体回退到 「唤出真实顶栏 + 点它的按钮」,绝不出现点了没反应的死按钮。
- 布局兜底:GUI 自带内联
min-width:1024/flex-basis:596px,一旦样式表失效 就会顶开页面。除样式表外,还用内联!important把关键物理约束写进 DOM,并由 400ms 轮询 +MutationObserver/ResizeObserver(rAF 去抖)持续纠偏,停用时逐条撤销。 - 工具箱换侧与积木区:用两引擎共同支持的
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维护), CSS100%会让滚动条与 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/。