Skip to content

Mini App pop-out window stays opaque under the webview renderer backend #597

Open

Description

@7723qqq

What happens

A Mini App that declares a transparent pop-out window renders as an opaque black rectangle when the client uses the webview renderer backend. The same package, unchanged, is fully transparent on the native web-contents-view backend.

Environment

  • MiniMax Code 3.1.12.216
  • Windows 11, build 26300 (UBR 9818)
  • Package installed under %USERPROFILE%\.minimax\plugins\<plugin>
  • miniapp.json:
"surface": {
  "path": "/bottle",
  "window": {
    "width": 220,
    "height": 300,
    "minWidth": 220,
    "minHeight": 300,
    "resizable": false,
    "frame": false,
    "transparent": true,
    "alwaysOnTop": true,
    "skipTaskbar": false
  }
}

Steps

  1. Install the package, open it in the side panel, pop it out into its own window.
  2. The window is a black rectangle around the page content, and it takes the mouse over its whole area.
  3. The client log confirms the backend:
[EmbeddedBrowserLifecycle] shown {"rendererBackend":"webview","requestedRendererBackend":"webview",
  "backendPolicyRevision":1,"event":"shown", ... "presentation":"detached"}

The page is not painting the backdrop

  • In window mode the page forces html, body and the app root to background: transparent !important, and the stage, panels and footer are display: none.
  • A headless Chromium render of the same page at 220×300 has alpha = 0 at all four corners, so the document paints nothing.
  • With the native backend the identical package is transparent, so the difference is the guest surface, not the document.

Where it looks broken

embedded-browser-manager.js passes the declaration through when it rebuilds the guest for a detach:

transparentBackground: host === tab.detachedWindow && tab.miniApp?.window?.transparent === true,

and webview-host/host.js only applies it as CSS on the host element:

Object.assign(entry.page.style, {
  colorScheme: "light",
  backgroundColor: descriptor.transparentBackground ? "transparent" : "Canvas"
});

The guest's own surface keeps an opaque background, so a transparent CSS background on the <webview> element cannot reveal what is behind the window.

Expected

A window declared "transparent": true should show the desktop behind the page's transparent pixels, as it does with web-contents-view.

Suggested fix

  • Give the guest a transparent background where the platform supports one (a transparent webContents background / WebView2 default background), instead of relying on CSS on the host element; or
  • Keep transparent: true Mini Apps on the native backend and state that the webview backend cannot honour the flag, so packages can avoid the declaration there.

Related

-webkit-app-region: drag is inert inside the webview guest as well, so a popped-out page has to move its own window through window.mcode.miniapp.window.update({ x, y }). That works, but docs/client.md recommends the app-region, and a page that follows the documentation ends up with a window the user cannot move at all.

Activity

  1. MyPrototypeWhat commented on Nov 9, 2026

    @MyPrototypeWhat

    Thanks for the detailed report. Could you share the Mini App's source code (a repository link or ZIP), or a minimal reproducible package that exhibits the issue?

    It would be especially helpful to include:

    • miniapp.json
    • The frontend code that applies the transparent styles and initializes or updates the inline/window mode

    We'd like to reproduce with the same package and distinguish page initialization after the WebView pop-out reload from a rendering/compositing issue. We haven't confirmed the root cause yet.

  2. 7723qqq commented on Nov 9, 2026

    @7723qqq
    Author

    谢谢。这个包是 MiniMax-Code-MiniApps 的 quota-bottle,对应 PR #116:

    补充一个复现动作:额度瓶开启悬浮窗模式后,切到“新建任务”界面时就会出现这个问题。

    客户端是一个静态 HTML 文件,Node 入口只负责提供页面和额度状态,渲染完全在客户端。

    miniapp.json:

    {
      "schemaVersion": 1,
      "artifacts": {
        "client": ["./miniapp/client"],
        "node": ["./miniapp/node"]
      },
      "runtime": {
        "kind": "process",
        "entry": "./miniapp/node/server.mjs",
        "lifecycle": "on-demand"
      },
      "surface": {
        "path": "/bottle",
        "window": {
          "width": 220,
          "height": 300,
          "minWidth": 220,
          "minHeight": 300,
          "resizable": false,
          "frame": false,
          "transparent": true,
          "alwaysOnTop": true,
          "skipTaskbar": false
        }
      },
      "mcpEndpoints": []
    }

    透明样式完全靠 CSS,由 data-mode 切换:

    body { background: var(--bg); }
    .stage { background: var(--panel); box-shadow: var(--shadow); }
    
    :root[data-mode="window"] { height: 100%; background: transparent !important; }
    :root[data-mode="window"] body,
    :root[data-mode="window"] .app { background: transparent !important; }
    :root[data-mode="window"] body { height: 100%; overflow: hidden; background: transparent; }
    :root[data-mode="window"] .stage {
      overflow: visible;
      padding: 0;
      border: 0;
      border-radius: 0;
      background: transparent;
      box-shadow: none;
    }
    :root[data-mode="window"] .stage::before { display: none; }

    模式初始化:

    <html lang="en" data-theme="light" data-mode="inline">
    const windowHandle = window.mcode?.miniapp?.window ?? null;
    
    const noteState = (state) => {
      // ...
      applyMode(state && state.mode === 'window' ? 'window' : 'inline');
    };
    
    windowHandle.getState().then(noteState).catch(() => {});
    windowHandle.onDidChangeState?.(noteState);

    点击固定按钮时:

    windowHandle.update({ mode: 'window' })
      .then(() => applyMode('window'))
      .catch(() => { /* 显示错误 */ });

    applyMode() 会设置 document.documentElement.dataset.mode = next。

    这里有两个可能,正好可以区分:

    1. 弹出窗口重新加载时,如果内联脚本执行时 window.mcode.miniapp.window 还没注入,windowHandle 就是 null,页面会一直停在 data-mode="inline",body 和 .stage 仍是不透明背景。
    2. 即使 bridge 已注入,页面也会先以 inline 渲染,等 getState() resolve 后才设置 data-mode="window"。如果 WebView 捕获了第一帧,可能把不透明的 inline 帧合成进去。

    快速判断:在弹出窗口里看 document.documentElement.dataset.mode 和 Boolean(window.mcode?.miniapp?.window)。如果 mode 一直是 inline,就是初始化路径;如果 mode 是 window、CSS 已经透明但窗口表面还是黑的,就是 WebView 合成层的问题。

    需要的话我可以再补一个最小 ZIP,或者一个初始 HTML 就写 data-mode="window" 的对照版本。

  3. MyPrototypeWhat commented on Nov 9, 2026

    @MyPrototypeWhat

    Thanks for sharing the source and the additional reproduction step. Could you try the comparison you suggested by changing only the initial data-mode in the HTML to window?

    -<html lang="en" data-theme="light" data-mode="inline">
    +<html lang="en" data-theme="light" data-mode="window">

    Then reopen the Mini App, enable the floating window, and switch to New Task (新建任务). Does the black background still appear?

    This is a diagnostic comparison to check whether the initial rendering mode affects the symptom; we haven't confirmed it as a fix.

  4. 7723qqq commented on Nov 9, 2026

    @7723qqq
    Author

    补了一个 ZIP,里面是当前 1.15.3 的实际包(排除了 tests,不含凭据和 dataDir 状态),外加 REPRO-597.md 和一张悬浮窗黑底截图:

    https://github.com/7723qqq/MiniMax-Code-MiniApps/releases/download/issue-597-repro-20261109/quota-bottle-issue-597.zip

    安装方式:把 ZIP 里的 quota-bottle/ 整个目录(连同 .minimax-plugin/)放到 MiniMax Code 的插件目录,例如 ~/.minimax/plugins/quota-bottle/,重启后打开额度瓶。

    对照结果:只把初始 data-mode 改成 window,重新打开 Mini App、固定悬浮窗、切到“新建任务”后,黑底仍然存在。所以问题不在初始 inline 首帧;更像是 WebView surface/合成层本身不透明。REPRO-597.md 里记了步骤、改动和这个结果。

    需要的话我可以再补一个只有 miniapp.json + 页面 + 假 /api/state 的极小包,排除真实额度和登录路径。

  5. MyPrototypeWhat commented on Nov 9, 2026

    @MyPrototypeWhat

    Thanks for testing the initial-mode comparison. I've attached a minimal diagnostic Mini App:

    issue597-transparent-probe-0.1.0.zip

    It keeps the same 220 × 300 frameless, transparent, non-resizable, always-on-top window settings. The page is permanently transparent from its first frame and only shows a cyan square and two buttons. It has no quota/login logic, mode-dependent background styles, state subscriptions, filters, or SVG.

    Could you try the following?

    1. Extract the ZIP and copy the entire issue597-transparent-probe/ folder, including .minimax-plugin/, into your MiniMax Code plugins directory, then restart the client.
    2. Open 透明窗口诊断 Mini App pop-out window stays opaque under the webview renderer backend #597 and confirm that the cyan square appears.
    3. Click 弹出独立窗口 and confirm the switch if prompted.
    4. Keep the floating window open, then switch the main window to New Task (新建任务). Please test with DevTools closed.

    Does the black background still appear? Please let us know whether it is transparent before switching, what changes afterward, and whether the cyan square remains visible.

    The Node entry and isolated macOS Electron transparency checks passed. The Windows reproduction is still unverified; this package is a diagnostic comparison, not a fix.

  6. 7723qqq commented on Nov 9, 2026

    @7723qqq
    Author

    补充一个观察:诊断页在主面板里周围的黑底/白底不是页面自己画的,而是 MiniMax Code 的面板底色,会跟随客户端的明暗模式变化(暗色下是黑的,亮色下是白的)。诊断页本身从第一帧就是 background: transparent !important,没有根据 data-mode 改背景。

    所以主面板截图里的黑色区域不能当作悬浮窗黑底的证据;悬浮窗切换前后的结果我还在按步骤测,出来后再单独回报。

  7. 7723qqq commented on Nov 9, 2026

    @7723qqq
    Author

    更正上一条,我重新看了截图:诊断页自己的区域在暗色下是黑底、亮色下是白底,并且把后面的输入框/网站卡片遮住了。这不是背后主面板的底色,而是诊断页的渲染表面本身不透明;青色方块和两个按钮仍可见,但周围是一整块纯色。

    诊断页的 CSS 是 background: transparent !important,没有任何主题切换逻辑,但实际 surface 仍带主题色。这个现象更像是 WebView surface/合成层不透明,而不是初始 inline 首帧或页面样式。上一条判断错了,抱歉。

    悬浮窗切换前后的结果我还在按步骤测,出来后再补。

  8. 7723qqq commented on Nov 9, 2026

    @7723qqq
    Author

    按诊断包测了:

    • 只点“弹出独立窗口”:悬浮窗是透明的,没有底色,青色方块和两个按钮正常。
    • 保持悬浮窗,把主窗口切到“新建任务”:悬浮窗里出现一个底色(当前暗色模式下是黑色),青色方块和两个按钮仍在。
    • 再切回其他页面时,底色是否消失我还没测;如果需要我可以补。

    触发点是“主窗口切到新建任务”,不是弹出独立窗口本身。这个结果支持 WebView surface/合成层在页面切换时变成不透明,而不是页面初始 inline 帧或页面 CSS。

  9. 7723qqq commented on Nov 9, 2026

    @7723qqq
    Author

    再补一个结果:只要切到“新建任务”一次,底色就出现;之后再切到其他页面(截图里已经切回“未命名会话”),底色仍然存在。

    所以这不是 New Task 页面专属,而是触发一次后悬浮窗 surface 一直保持不透明,属于粘性状态。触发点看起来是主窗口第一次切到“新建任务”的那一刻。

  10. 7723qqq commented on Nov 9, 2026

    @7723qqq
    Author

    再补一个触发页面:除了“新建任务”,切到“设置”页面也会出现同样的底色,触发后同样会保持。

    所以触发面不止 New Task;看起来是主窗口切到某些全页路由(新建任务、设置)时,WebView surface 的不透明度/合成状态被改掉,并且之后不会自己恢复。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions