Compare commits
4
Commits
64db6abbfb
...
bce20d9e4d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bce20d9e4d | ||
|
|
171602f302 | ||
|
|
6fbb9c077e | ||
|
|
813d1ca02e |
No files matched your search
@@ -4,6 +4,21 @@ This file is the integrated default-branch snapshot. Feature tasks record progre
|
||||
|
||||
## Integrated Through
|
||||
|
||||
- Native text context-menu source frontier
|
||||
`6fbb9c077ecda23e2ee6472631fe374527c49cb7` from task
|
||||
`20260907-input-context-menu-a7c31e` is fast-forwarded onto local `main` by
|
||||
task `20260907-integrate-context-menu-5a9e3c71`. The primary Renderer window
|
||||
now receives one Electron Main-owned native menu for editable controls or an
|
||||
existing text selection, exposing Chinese Cut, Copy, Paste, and Select All
|
||||
roles according to Chromium edit capabilities. The same window-level listener
|
||||
covers both Code and AI Design textareas without Renderer clipboard IPC or a
|
||||
duplicate Canvas listener; ordinary non-editable content remains unaffected.
|
||||
Verification passed the 5 focused unit cases, typecheck, full lint with 0
|
||||
errors and 5 existing warnings, all Vite targets, and an Electron E2E that
|
||||
observed one popup for each Composer. The full unit run passed 1,938 tests with
|
||||
2 skips; its only failure was the unchanged Pi real-process two-second timing
|
||||
assertion, which passed all 6 tests in isolation. No push, package, deployment,
|
||||
publication, Provider call, or user-data mutation was performed.
|
||||
- Local AI Design activity frontier
|
||||
`4b894c7e320cdf829888f63f501d176f051be7d7` and fetched remote frontier
|
||||
`918f8f80dca61b0f561f96b168e4ef541734d414` are reconciled by the ordinary
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# Task: Fix AI programming input context menu
|
||||
|
||||
## Identity
|
||||
|
||||
- Task ID: 20260907-input-context-menu-a7c31e
|
||||
- Mode: Feature
|
||||
- Branch: codex/20260907-input-context-menu-a7c31e-input-context-menu
|
||||
- Worktree: D:\Datas\OthersProjects\.codex-worktrees\makelore\20260907-input-context-menu-a7c31e
|
||||
- Base commit: 8c13ae76db6fa47972bdf9ff3de932eb761aaffe
|
||||
- Owner: codex
|
||||
- Status: Ready for Integration
|
||||
|
||||
## Scope
|
||||
|
||||
- Reproduce the AI Programming composer symptom where right-clicking editable text
|
||||
exposes no native editing menu.
|
||||
- Compare the current integrated primary-window wiring with completed task
|
||||
`20260819-context-menu-impl-c7d4a2e1` and reuse only behavior still compatible
|
||||
with the current window lifecycle.
|
||||
- Restore the smallest Main-owned native text-editing context-menu behavior and add
|
||||
focused regression coverage at the real window/event seam.
|
||||
- Verify that the AI Design `design-chat-composer` textarea shares the same
|
||||
primary Renderer seam, and extend the Electron regression to cover both Code
|
||||
and Design textarea controls without adding a duplicate listener.
|
||||
- Update user-facing documentation or Electron E2E coverage only where current
|
||||
behavior and the shared fixture require it.
|
||||
|
||||
## Intent And Constraints
|
||||
|
||||
- Electron Main owns desktop context-menu integration. Do not add Renderer,
|
||||
Preload, IPC, Host API, or direct clipboard access for this fix.
|
||||
- Use the primary window's `webContents` `context-menu` event and native editing
|
||||
roles when that remains the confirmed compatible seam.
|
||||
- Show a menu only for editable controls or an existing text selection; derive
|
||||
enabled actions from the actual Electron edit context.
|
||||
- Preserve Renderer-owned custom menus, Monaco behavior, the detached Agent Browser,
|
||||
existing application-menu behavior, Chinese-only UI, and the single light visual
|
||||
system.
|
||||
- Build a deterministic red-capable feedback loop for the reported input symptom
|
||||
before selecting a cause or changing production code.
|
||||
- Work only in the isolated task worktree; do not modify, adopt, clean, or merge the
|
||||
occupied root `main` worktree or any peer worktree.
|
||||
|
||||
## Outcome
|
||||
|
||||
- Confirmed the failure at the real Electron window seam: a renderer right-click
|
||||
produced no native menu because the primary `webContents` had zero
|
||||
`context-menu` listeners. The earlier implementation in task
|
||||
`20260819-context-menu-impl-c7d4a2e1` exists only on its isolated branch and
|
||||
was never integrated into the current `main` history.
|
||||
- Added a Main-owned text context-menu module that builds native Chinese
|
||||
`cut`, `copy`, `paste`, and `selectAll` roles from Electron's actual
|
||||
`isEditable`, `selectionText`, and `editFlags` context. Ordinary non-editable
|
||||
content still produces no menu.
|
||||
- Registered the listener once during primary-window creation without adding
|
||||
Renderer, Preload, IPC, Host API, or direct clipboard access.
|
||||
- Confirmed `DesignConversationPane` renders the AI Design composer as the
|
||||
ordinary `<textarea id="design-chat-composer">` produced by the shared
|
||||
`Textarea` component inside that same primary Renderer. The window-level
|
||||
listener therefore covers both AI Code and AI Design without a second
|
||||
production listener.
|
||||
- Added five focused unit cases and an Electron E2E regression that dispatches
|
||||
real right-clicks on Code and Design textarea controls inside the primary
|
||||
Renderer window and observes two native menu popups with the expected editing
|
||||
actions.
|
||||
- Synchronized `README.md` with the restored desktop behavior and ownership
|
||||
boundary.
|
||||
|
||||
## Verification
|
||||
|
||||
- Red phase:
|
||||
- `corepack pnpm exec playwright test tests/e2e/context-menu.spec.ts` failed
|
||||
twice with `popupCount: 0` before the production change.
|
||||
- A temporary targeted probe reported zero `context-menu` listeners on the
|
||||
primary `webContents`; the probe was removed before completion.
|
||||
- `corepack pnpm exec vitest run tests/unit/context-menu.test.ts` failed to
|
||||
resolve the absent Main module before implementation.
|
||||
- Green phase:
|
||||
- `corepack pnpm exec vitest run tests/unit/context-menu.test.ts` — 5 passed.
|
||||
- `corepack pnpm exec playwright test tests/e2e/context-menu.spec.ts` — 1
|
||||
passed, covering both Code and Design textareas with two observed native
|
||||
popups.
|
||||
- `corepack pnpm run typecheck` — passed.
|
||||
- `corepack pnpm exec eslint electron/main/context-menu.ts electron/main/index.ts tests/unit/context-menu.test.ts tests/e2e/context-menu.spec.ts`
|
||||
— passed.
|
||||
- `corepack pnpm run lint:check` — passed with 5 pre-existing warnings in
|
||||
untouched files and no errors.
|
||||
- `corepack pnpm run build:vite` — passed for Renderer, Main, Preload, and
|
||||
release utility bundles; only existing chunk/dynamic-import warnings were
|
||||
reported.
|
||||
- `corepack pnpm test` — 226 test files passed; one unrelated real-process
|
||||
timing assertion in `pi-agent-server-process-real.test.ts` measured 2323ms
|
||||
against a 2000ms threshold. Its isolated rerun with
|
||||
`corepack pnpm exec vitest run tests/unit/pi-agent-server-process-real.test.ts --maxWorkers=1`
|
||||
passed all 6 tests, so no unrelated timing code or threshold was changed.
|
||||
- `git diff --check` — passed.
|
||||
|
||||
## Follow-ups
|
||||
|
||||
- Integrate this ready branch through the project Integration Gate after the
|
||||
current owner of the root `main` worktree releases it.
|
||||
|
||||
## Promotion Candidates
|
||||
|
||||
- None recorded.
|
||||
@@ -0,0 +1,88 @@
|
||||
# Task: Integrate native text context menu
|
||||
|
||||
## Identity
|
||||
|
||||
- Task ID: 20260907-integrate-context-menu-5a9e3c71
|
||||
- Mode: Integration
|
||||
- Branch: main
|
||||
- Worktree: D:\Datas\OthersProjects\makelore
|
||||
- Base commit: 6fbb9c077ecda23e2ee6472631fe374527c49cb7
|
||||
- Owner: codex-root
|
||||
- Status: Ready for Integration
|
||||
|
||||
## Scope
|
||||
|
||||
- Integrate the reviewed source branch
|
||||
`codex/20260907-input-context-menu-a7c31e-input-context-menu@6fbb9c0` onto
|
||||
local `main`.
|
||||
- Integrate the Main-owned native text context menu for editable controls and
|
||||
selected text, including explicit Code and AI Design textarea coverage.
|
||||
- Re-run focused verification on merged `main`, update the canonical integrated
|
||||
snapshot, and retain the source task record in source history unchanged.
|
||||
|
||||
## Intent And Constraints
|
||||
|
||||
- Preserve the source task's Electron Main ownership boundary: no Renderer,
|
||||
Preload, IPC, Host API, or direct clipboard surface is added.
|
||||
- Preserve the three explicitly adopted untracked task records byte-for-byte;
|
||||
do not add, edit, delete, move, or commit them.
|
||||
- Do not merge the superseded August implementation branch separately; the
|
||||
current source already revalidates that behavior against the latest window
|
||||
lifecycle and covers both Code and Design textareas.
|
||||
- Do not change unrelated Pi timing code or thresholds, package, deploy,
|
||||
publish, push to `origin`, or alter user data as part of this integration.
|
||||
|
||||
## Outcome
|
||||
|
||||
- After the user's explicit confirmation, released the completed prior
|
||||
Integration ownership and claimed `main` while adopting the three known
|
||||
untracked task records by fingerprint. Their files remain byte-for-byte
|
||||
untouched and outside this integration commit.
|
||||
- Verified that local `main@8c13ae7` was an ancestor of the reviewed source tip
|
||||
`6fbb9c0`, then fast-forwarded `main` without conflicts or history rewriting.
|
||||
- Re-established this Integration task's comparison base at the accepted source
|
||||
tip using the bundled ownership scripts, so the source task record remains
|
||||
read-only source history rather than being attributed to this task.
|
||||
- Integrated one Electron Main-owned native text menu for editable controls and
|
||||
selected text. Code and AI Design textarea controls share the same primary
|
||||
Renderer listener; no duplicate Canvas listener or Renderer clipboard surface
|
||||
was introduced.
|
||||
- Retained the source task record and README update, and recorded the source tip
|
||||
under `Current State / Integrated Through`. No additional ADR or architecture
|
||||
change was required.
|
||||
|
||||
## Verification
|
||||
|
||||
- Before fast-forward, the source tip matched the reviewed
|
||||
`6fbb9c077ecda23e2ee6472631fe374527c49cb7`, `git merge-base --is-ancestor`
|
||||
passed, and `git diff --check` reported no source whitespace errors.
|
||||
- `corepack pnpm --version` — exact pinned `10.33.4`.
|
||||
- `corepack pnpm exec vitest run tests/unit/context-menu.test.ts` — 5 passed.
|
||||
- `corepack pnpm run typecheck` — passed.
|
||||
- Focused ESLint for the Main module, window registration, unit test, and E2E —
|
||||
passed with no output.
|
||||
- `corepack pnpm run build:vite` — Renderer, Main, Preload, and release utility
|
||||
bundles passed; only existing Browserslist, mixed-import, and chunk-size
|
||||
warnings were emitted.
|
||||
- `corepack pnpm exec playwright test tests/e2e/context-menu.spec.ts` — 1 passed,
|
||||
observing two native popups across the Code and AI Design textarea controls.
|
||||
- `corepack pnpm run lint:check` — passed with 0 errors and 5 existing warnings
|
||||
in untouched files.
|
||||
- `corepack pnpm test` — 226 files / 1,938 tests passed with 2 skipped; the only
|
||||
failure was the unchanged Pi real-process two-second timing assertion (2112ms
|
||||
against 2000ms in the final report). Its isolated rerun with one worker passed
|
||||
all 6 tests, so no unrelated Pi code or threshold was changed.
|
||||
- Task-aware documentation drift and adopted-record fingerprint checks passed;
|
||||
`git diff --check` also passed.
|
||||
|
||||
## Follow-ups
|
||||
|
||||
- A rebuilt or development client is required to observe the newly integrated
|
||||
Main bundle; this task does not package or publish an installer.
|
||||
- Push `main` to `origin` only if the user separately requests publication.
|
||||
|
||||
## Promotion Candidates
|
||||
|
||||
- Applied in this integration: `current-state.md` records the source frontier,
|
||||
Main ownership boundary, shared Code/Design behavior, and verification result.
|
||||
No further promotion is pending.
|
||||
@@ -15,6 +15,7 @@ Makelore 是一个面向软件、视觉创作与智能机器人的 AI 桌面工
|
||||
## 当前产品状态
|
||||
|
||||
- 桌面技术栈:Electron、React 19、Vite、TypeScript、Zustand、Tailwind CSS。
|
||||
- 桌面文本右键菜单:主 Renderer 窗口由 Electron Main 提供原生“剪切、复制、粘贴、全选”操作;菜单只在可编辑区域或已有文本选择时出现,普通空白区域不显示,也不向 Renderer 暴露剪贴板 IPC。
|
||||
- AI 编程核心对话运行时:Electron Main 按需启动一个长驻 Pi Agent Server,同一进程内为每条 Conversation 建立独立的 Runtime、Session、凭证视图、扩展上下文和 JSONL 通道,同时复用 Pi 模块与进程启动成本;Server 异常退出时所有旧通道统一失效,下一次恢复只重启一个 Server。正式包中的 Agent Server 从明确传入的 `resources/pi-runtime` 根定位依赖清单与包入口,不从脚本所在目录或应用 `node_modules` 猜测解析。Renderer 不直接启动或调用 Pi,也不读取其 wire 类型。项目与 Conversation 元数据先从本地读取,选中对话后才按需准备对应逻辑线程;输入框不等待运行时准备完成。同一助手工具批次中,会取得项目写租约的 `bash`、`edit`、`write` 与动态产品工具按顺序执行,避免批次在命令启动前自锁;纯只读工具仍可并行。子 Agent 仍使用独立的短生命周期进程,以保留并发配额、故障和写入边界;macOS 上两类后台进程都使用 Electron 自带的 Helper,不在 Dock 中创建额外应用图标。
|
||||
- AI 编程默认入口:进入 Code 后保持在 `/chat`。尚未选择本地项目时,页面以麦洛 M 标识和居中引导语建立焦点,下方提供独立的“新增项目”主操作以及可横向浏览、直接进入的已有项目卡片;项目创建和进入仍复用侧栏现有流程。已有项目的新 Conversation 在可编辑 Composer 上方沿用同一品牌引导,第一条消息出现后切换为正常时间线。
|
||||
- AI 编程历史与配额反馈:Pi 上下文压缩不删除用户可见的 active-branch 历史,压缩摘要保持 Main-private;Renderer 首次渲染最近 120 个节点,向上滚动时按 100 个节点追加更早内容并保持阅读位置。Main 精确识别 `token_point_balance_exhausted` / `词元点数余额不足`,只向 Renderer 投影不可重试的稳定配额错误和安全中文提示,重开会话后仍可见且不暴露上游原始错误或 request id。
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Menu } from 'electron';
|
||||
import type { BrowserWindow } from 'electron';
|
||||
|
||||
export type TextContextMenuContext = Pick<
|
||||
Electron.ContextMenuParams,
|
||||
'isEditable' | 'selectionText' | 'editFlags'
|
||||
>;
|
||||
|
||||
export function createTextContextMenuTemplate(
|
||||
context: TextContextMenuContext,
|
||||
): Electron.MenuItemConstructorOptions[] | null {
|
||||
if (!context.isEditable && context.selectionText.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
label: '剪切',
|
||||
role: 'cut',
|
||||
enabled: context.isEditable && context.editFlags.canCut,
|
||||
},
|
||||
{
|
||||
label: '复制',
|
||||
role: 'copy',
|
||||
enabled: context.editFlags.canCopy,
|
||||
},
|
||||
{
|
||||
label: '粘贴',
|
||||
role: 'paste',
|
||||
enabled: context.isEditable && context.editFlags.canPaste,
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: '全选',
|
||||
role: 'selectAll',
|
||||
enabled: context.editFlags.canSelectAll,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function registerTextContextMenu(win: BrowserWindow): void {
|
||||
win.webContents.on('context-menu', (_event, params) => {
|
||||
const template = createTextContextMenuTemplate(params);
|
||||
if (!template) return;
|
||||
|
||||
Menu.buildFromTemplate(template).popup({
|
||||
window: win,
|
||||
frame: params.frame ?? undefined,
|
||||
sourceType: params.menuSourceType,
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { join } from 'node:path';
|
||||
import { registerIpcHandlers } from './ipc-handlers';
|
||||
import { createTray } from './tray';
|
||||
import { createMenu } from './menu';
|
||||
import { registerTextContextMenu } from './context-menu';
|
||||
import { registerZoomShortcuts } from './zoom-shortcuts';
|
||||
import { getNativeWindowMaterialOptions } from './window-material';
|
||||
|
||||
@@ -272,6 +273,7 @@ function createWindow(): BrowserWindow {
|
||||
});
|
||||
|
||||
installMediaPermissionHandler(win);
|
||||
registerTextContextMenu(win);
|
||||
registerZoomShortcuts(win);
|
||||
|
||||
// Handle external links — only allow safe protocols to prevent arbitrary
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import type { ElectronApplication } from '@playwright/test';
|
||||
import { closeElectronApp, expect, getStableWindow, test } from './fixtures/electron';
|
||||
|
||||
type ContextMenuProbe = {
|
||||
popupCount: number;
|
||||
template: Array<{
|
||||
label: string | null;
|
||||
role: string | null;
|
||||
type: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
async function installContextMenuProbe(app: ElectronApplication): Promise<void> {
|
||||
await app.evaluate(({ Menu }) => {
|
||||
const probe: ContextMenuProbe = {
|
||||
popupCount: 0,
|
||||
template: [],
|
||||
};
|
||||
const globalWithProbe = globalThis as typeof globalThis & {
|
||||
__makeloreContextMenuProbe?: ContextMenuProbe;
|
||||
};
|
||||
globalWithProbe.__makeloreContextMenuProbe = probe;
|
||||
|
||||
Menu.buildFromTemplate = ((template) => {
|
||||
probe.template = template.map((item) => ({
|
||||
label: item.label ?? null,
|
||||
role: item.role ?? null,
|
||||
type: item.type ?? 'normal',
|
||||
}));
|
||||
|
||||
return {
|
||||
popup: () => {
|
||||
probe.popupCount += 1;
|
||||
},
|
||||
} as unknown as Electron.Menu;
|
||||
}) as typeof Menu.buildFromTemplate;
|
||||
});
|
||||
}
|
||||
|
||||
async function readContextMenuProbe(app: ElectronApplication): Promise<ContextMenuProbe | null> {
|
||||
return await app.evaluate(() => {
|
||||
const globalWithProbe = globalThis as typeof globalThis & {
|
||||
__makeloreContextMenuProbe?: ContextMenuProbe;
|
||||
};
|
||||
return globalWithProbe.__makeloreContextMenuProbe ?? null;
|
||||
});
|
||||
}
|
||||
|
||||
test.describe('Makelore text context menu', () => {
|
||||
test('opens native editing actions for Code and Design textareas', async ({ launchElectronApp }) => {
|
||||
const app = await launchElectronApp({ skipSetup: true });
|
||||
|
||||
try {
|
||||
const page = await getStableWindow(app);
|
||||
await installContextMenuProbe(app);
|
||||
await page.evaluate(() => {
|
||||
const composerIds = [
|
||||
'context-menu-e2e-code-composer',
|
||||
'design-chat-composer',
|
||||
];
|
||||
|
||||
for (const [index, id] of composerIds.entries()) {
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.id = id;
|
||||
textarea.style.position = 'fixed';
|
||||
textarea.style.inset = `${80 + index * 80}px auto auto 80px`;
|
||||
textarea.style.zIndex = '2147483647';
|
||||
document.body.append(textarea);
|
||||
}
|
||||
});
|
||||
|
||||
for (const selector of [
|
||||
'#context-menu-e2e-code-composer',
|
||||
'#design-chat-composer',
|
||||
]) {
|
||||
const textarea = page.locator(selector);
|
||||
await textarea.fill('Makelore');
|
||||
await textarea.selectText();
|
||||
await textarea.click({ button: 'right' });
|
||||
}
|
||||
|
||||
await expect.poll(async () => await readContextMenuProbe(app)).toEqual({
|
||||
popupCount: 2,
|
||||
template: [
|
||||
{ label: '剪切', role: 'cut', type: 'normal' },
|
||||
{ label: '复制', role: 'copy', type: 'normal' },
|
||||
{ label: '粘贴', role: 'paste', type: 'normal' },
|
||||
{ label: null, role: null, type: 'separator' },
|
||||
{ label: '全选', role: 'selectAll', type: 'normal' },
|
||||
],
|
||||
});
|
||||
} finally {
|
||||
await closeElectronApp(app);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,120 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
createTextContextMenuTemplate,
|
||||
type TextContextMenuContext,
|
||||
} from '@electron/main/context-menu';
|
||||
|
||||
type ContextOverrides = {
|
||||
isEditable?: boolean;
|
||||
selectionText?: string;
|
||||
editFlags?: Partial<Electron.EditFlags>;
|
||||
};
|
||||
|
||||
function context(overrides: ContextOverrides = {}): TextContextMenuContext {
|
||||
return {
|
||||
isEditable: overrides.isEditable ?? false,
|
||||
selectionText: overrides.selectionText ?? '',
|
||||
editFlags: {
|
||||
canUndo: false,
|
||||
canRedo: false,
|
||||
canCut: false,
|
||||
canCopy: false,
|
||||
canPaste: false,
|
||||
canDelete: false,
|
||||
canSelectAll: false,
|
||||
canEditRichly: false,
|
||||
...overrides.editFlags,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function enabledByRole(template: Electron.MenuItemConstructorOptions[] | null) {
|
||||
return Object.fromEntries(
|
||||
(template ?? [])
|
||||
.filter((item) => item.role)
|
||||
.map((item) => [item.role, item.enabled]),
|
||||
);
|
||||
}
|
||||
|
||||
describe('text context menu', () => {
|
||||
it('does not create a menu for ordinary non-editable content', () => {
|
||||
expect(createTextContextMenuTemplate(context())).toBeNull();
|
||||
});
|
||||
|
||||
it('allows paste and select all in an editable control without a selection', () => {
|
||||
const template = createTextContextMenuTemplate(context({
|
||||
isEditable: true,
|
||||
editFlags: {
|
||||
canPaste: true,
|
||||
canSelectAll: true,
|
||||
},
|
||||
}));
|
||||
|
||||
expect(template?.map((item) => item.label ?? item.type)).toEqual([
|
||||
'剪切',
|
||||
'复制',
|
||||
'粘贴',
|
||||
'separator',
|
||||
'全选',
|
||||
]);
|
||||
expect(enabledByRole(template)).toEqual({
|
||||
cut: false,
|
||||
copy: false,
|
||||
paste: true,
|
||||
selectAll: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('enables every requested action for an editable selection when supported', () => {
|
||||
const template = createTextContextMenuTemplate(context({
|
||||
isEditable: true,
|
||||
selectionText: 'Makelore',
|
||||
editFlags: {
|
||||
canCut: true,
|
||||
canCopy: true,
|
||||
canPaste: true,
|
||||
canSelectAll: true,
|
||||
},
|
||||
}));
|
||||
|
||||
expect(enabledByRole(template)).toEqual({
|
||||
cut: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
selectAll: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps cut and paste disabled for a read-only selection', () => {
|
||||
const template = createTextContextMenuTemplate(context({
|
||||
selectionText: 'Makelore',
|
||||
editFlags: {
|
||||
canCut: true,
|
||||
canCopy: true,
|
||||
canPaste: true,
|
||||
canSelectAll: true,
|
||||
},
|
||||
}));
|
||||
|
||||
expect(enabledByRole(template)).toEqual({
|
||||
cut: false,
|
||||
copy: true,
|
||||
paste: false,
|
||||
selectAll: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('respects disabled renderer editing capabilities', () => {
|
||||
const template = createTextContextMenuTemplate(context({
|
||||
isEditable: true,
|
||||
selectionText: 'Makelore',
|
||||
}));
|
||||
|
||||
expect(enabledByRole(template)).toEqual({
|
||||
cut: false,
|
||||
copy: false,
|
||||
paste: false,
|
||||
selectAll: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user