@jackwener/opencli 1.7.19 → 1.7.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -9
- package/README.zh-CN.md +9 -10
- package/cli-manifest.json +6 -177
- package/clis/twitter/bookmark-folder.js +5 -3
- package/clis/twitter/bookmark-folder.test.js +59 -1
- package/clis/twitter/bookmarks.js +9 -3
- package/clis/twitter/bookmarks.test.js +205 -0
- package/dist/src/browser/daemon-client.d.ts +1 -0
- package/dist/src/browser/daemon-client.js +3 -0
- package/dist/src/browser/daemon-client.test.js +20 -0
- package/dist/src/cli.js +8 -3
- package/dist/src/cli.test.js +1 -0
- package/dist/src/daemon-utils.d.ts +18 -0
- package/dist/src/daemon-utils.js +37 -0
- package/dist/src/daemon.d.ts +1 -1
- package/dist/src/daemon.js +44 -13
- package/dist/src/daemon.test.js +42 -1
- package/dist/src/electron-apps.js +0 -1
- package/dist/src/electron-apps.test.js +1 -0
- package/dist/src/external-clis.yaml +12 -3
- package/dist/src/external.d.ts +4 -0
- package/dist/src/external.js +3 -0
- package/dist/src/external.test.js +24 -1
- package/dist/src/help.d.ts +5 -1
- package/dist/src/help.js +4 -3
- package/dist/src/help.test.js +5 -1
- package/package.json +1 -1
- package/clis/notion/export.js +0 -32
- package/clis/notion/favorites.js +0 -85
- package/clis/notion/new.js +0 -35
- package/clis/notion/read.js +0 -31
- package/clis/notion/search.js +0 -47
- package/clis/notion/sidebar.js +0 -42
- package/clis/notion/status.js +0 -17
- package/clis/notion/write.js +0 -41
package/dist/src/help.d.ts
CHANGED
|
@@ -40,12 +40,16 @@ export type AdapterKind = 'site' | 'app';
|
|
|
40
40
|
export declare function classifyAdapter(domain: string | undefined): AdapterKind;
|
|
41
41
|
export interface RootAdapterGroups {
|
|
42
42
|
/** Externally-registered CLIs (docker, gh, vercel, ...) — passthrough binaries */
|
|
43
|
-
external: readonly
|
|
43
|
+
external: readonly RootExternalCli[];
|
|
44
44
|
/** Desktop-app adapters (chatgpt-app, chatwise, codex, ...) */
|
|
45
45
|
apps: readonly string[];
|
|
46
46
|
/** Web-site adapters (bilibili, dianping, ...) */
|
|
47
47
|
sites: readonly string[];
|
|
48
48
|
}
|
|
49
|
+
export interface RootExternalCli {
|
|
50
|
+
name: string;
|
|
51
|
+
label: string;
|
|
52
|
+
}
|
|
49
53
|
export declare function formatRootAdapterHelpText(groups: RootAdapterGroups): string;
|
|
50
54
|
/**
|
|
51
55
|
* Extracts a positional placeholder that should appear immediately after this
|
package/dist/src/help.js
CHANGED
|
@@ -116,7 +116,7 @@ export function formatRootAdapterHelpText(groups) {
|
|
|
116
116
|
if (total === 0)
|
|
117
117
|
return '';
|
|
118
118
|
const lines = [''];
|
|
119
|
-
lines.push(...formatGroupSection('External CLIs', groups.external));
|
|
119
|
+
lines.push(...formatGroupSection('External CLIs', groups.external.map(cli => cli.label)));
|
|
120
120
|
lines.push(...formatGroupSection('App adapters', groups.apps));
|
|
121
121
|
lines.push(...formatGroupSection('Site adapters', groups.sites));
|
|
122
122
|
lines.push("Run 'opencli list' for full command details, or 'opencli <site> --help' to inspect one site.");
|
|
@@ -376,7 +376,7 @@ function compactCommand(cmd) {
|
|
|
376
376
|
};
|
|
377
377
|
}
|
|
378
378
|
export function rootHelpData(program, groups) {
|
|
379
|
-
const adapterNames = new Set([...groups.external, ...groups.apps, ...groups.sites]);
|
|
379
|
+
const adapterNames = new Set([...groups.external.map(cli => cli.name), ...groups.apps, ...groups.sites]);
|
|
380
380
|
const commands = program.commands
|
|
381
381
|
.filter(command => !adapterNames.has(command.name()))
|
|
382
382
|
.map(command => ({
|
|
@@ -390,7 +390,8 @@ export function rootHelpData(program, groups) {
|
|
|
390
390
|
commands,
|
|
391
391
|
external_clis: {
|
|
392
392
|
count: groups.external.length,
|
|
393
|
-
clis:
|
|
393
|
+
clis: groups.external.map(cli => cli.name).sort(sortLocale),
|
|
394
|
+
display: groups.external.map(cli => cli.label).sort(sortLocale),
|
|
394
395
|
},
|
|
395
396
|
app_adapters: {
|
|
396
397
|
count: groups.apps.length,
|
package/dist/src/help.test.js
CHANGED
|
@@ -20,13 +20,17 @@ describe('classifyAdapter', () => {
|
|
|
20
20
|
describe('formatRootAdapterHelpText', () => {
|
|
21
21
|
it('renders all three sections in External / App / Site order when populated', () => {
|
|
22
22
|
const text = formatRootAdapterHelpText({
|
|
23
|
-
external: [
|
|
23
|
+
external: [
|
|
24
|
+
{ name: 'gh', label: 'gh' },
|
|
25
|
+
{ name: 'wx', label: 'wx(wx-cli)' },
|
|
26
|
+
],
|
|
24
27
|
apps: ['chatwise', 'codex'],
|
|
25
28
|
sites: ['bilibili'],
|
|
26
29
|
});
|
|
27
30
|
expect(text).toContain('External CLIs (2):');
|
|
28
31
|
expect(text).toContain('App adapters (2):');
|
|
29
32
|
expect(text).toContain('Site adapters (1):');
|
|
33
|
+
expect(text).toContain('wx(wx-cli)');
|
|
30
34
|
expect(text.indexOf('External CLIs')).toBeLessThan(text.indexOf('App adapters'));
|
|
31
35
|
expect(text.indexOf('App adapters')).toBeLessThan(text.indexOf('Site adapters'));
|
|
32
36
|
});
|
package/package.json
CHANGED
package/clis/notion/export.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
3
|
-
export const exportCommand = cli({
|
|
4
|
-
site: 'notion',
|
|
5
|
-
name: 'export',
|
|
6
|
-
access: 'read',
|
|
7
|
-
description: 'Export the current Notion page as Markdown',
|
|
8
|
-
domain: 'localhost',
|
|
9
|
-
strategy: Strategy.UI,
|
|
10
|
-
browser: true,
|
|
11
|
-
args: [
|
|
12
|
-
{ name: 'output', required: false, help: 'Output file (default: /tmp/notion-export.md)' },
|
|
13
|
-
],
|
|
14
|
-
columns: ['Status', 'File'],
|
|
15
|
-
func: async (page, kwargs) => {
|
|
16
|
-
const outputPath = kwargs.output || '/tmp/notion-export.md';
|
|
17
|
-
const result = await page.evaluate(`
|
|
18
|
-
(function() {
|
|
19
|
-
const titleEl = document.querySelector('[data-block-id] [placeholder="Untitled"], h1.notion-title, [class*="title"]');
|
|
20
|
-
const title = titleEl ? (titleEl.textContent || '').trim() : document.title;
|
|
21
|
-
|
|
22
|
-
const frame = document.querySelector('.notion-page-content, [class*="page-content"], main');
|
|
23
|
-
const content = frame ? (frame.innerText || '').trim() : document.body.innerText;
|
|
24
|
-
|
|
25
|
-
return { title, content };
|
|
26
|
-
})()
|
|
27
|
-
`);
|
|
28
|
-
const md = `# ${result.title}\n\n${result.content}`;
|
|
29
|
-
fs.writeFileSync(outputPath, md);
|
|
30
|
-
return [{ Status: 'Success', File: outputPath }];
|
|
31
|
-
},
|
|
32
|
-
});
|
package/clis/notion/favorites.js
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
2
|
-
export const favoritesCommand = cli({
|
|
3
|
-
site: 'notion',
|
|
4
|
-
name: 'favorites',
|
|
5
|
-
access: 'read',
|
|
6
|
-
description: 'List pages from the Notion Favorites section in the sidebar',
|
|
7
|
-
domain: 'localhost',
|
|
8
|
-
strategy: Strategy.UI,
|
|
9
|
-
browser: true,
|
|
10
|
-
args: [],
|
|
11
|
-
columns: ['Index', 'Title', 'Icon'],
|
|
12
|
-
func: async (page) => {
|
|
13
|
-
const items = await page.evaluate(`
|
|
14
|
-
(function() {
|
|
15
|
-
const results = [];
|
|
16
|
-
|
|
17
|
-
// Strategy 1: Use Notion's own class 'notion-outliner-bookmarks-header-container'
|
|
18
|
-
const headerContainer = document.querySelector('.notion-outliner-bookmarks-header-container');
|
|
19
|
-
if (headerContainer) {
|
|
20
|
-
// Walk up to the section parent that wraps header + items
|
|
21
|
-
let section = headerContainer.parentElement;
|
|
22
|
-
if (section && section.children.length === 1) section = section.parentElement;
|
|
23
|
-
|
|
24
|
-
if (section) {
|
|
25
|
-
const treeItems = section.querySelectorAll('[role="treeitem"]');
|
|
26
|
-
treeItems.forEach((item) => {
|
|
27
|
-
// Title text is in a div.notranslate sibling of the icon area
|
|
28
|
-
const titleEl = item.querySelector('div.notranslate:not(.notion-record-icon)');
|
|
29
|
-
const title = titleEl
|
|
30
|
-
? titleEl.textContent.trim()
|
|
31
|
-
: (item.textContent || '').trim().substring(0, 80);
|
|
32
|
-
|
|
33
|
-
// Icon/emoji is in the notion-record-icon element
|
|
34
|
-
const iconEl = item.querySelector('.notion-record-icon');
|
|
35
|
-
const icon = iconEl ? iconEl.textContent.trim().substring(0, 4) : '';
|
|
36
|
-
|
|
37
|
-
if (title && title.length > 0) {
|
|
38
|
-
results.push({ Index: results.length + 1, Title: title, Icon: icon || '📄' });
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Strategy 2: Fallback — find "Favorites" text node and walk DOM
|
|
45
|
-
if (results.length === 0) {
|
|
46
|
-
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null);
|
|
47
|
-
let node;
|
|
48
|
-
let favEl = null;
|
|
49
|
-
while (node = walker.nextNode()) {
|
|
50
|
-
const text = node.textContent.trim();
|
|
51
|
-
if (text === 'Favorites' || text === '收藏' || text === '收藏夹') {
|
|
52
|
-
favEl = node.parentElement;
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (favEl) {
|
|
58
|
-
let section = favEl;
|
|
59
|
-
for (let i = 0; i < 6; i++) {
|
|
60
|
-
const p = section.parentElement;
|
|
61
|
-
if (!p || p === document.body) break;
|
|
62
|
-
const treeItems = p.querySelectorAll(':scope > [role="treeitem"]');
|
|
63
|
-
if (treeItems.length > 0) { section = p; break; }
|
|
64
|
-
section = p;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const treeItems = section.querySelectorAll('[role="treeitem"]');
|
|
68
|
-
treeItems.forEach((item) => {
|
|
69
|
-
const text = (item.textContent || '').trim().substring(0, 120);
|
|
70
|
-
if (text && text.length > 1 && !text.match(/^(Favorites|收藏夹?)$/)) {
|
|
71
|
-
results.push({ Index: results.length + 1, Title: text, Icon: '📄' });
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return results;
|
|
78
|
-
})()
|
|
79
|
-
`);
|
|
80
|
-
if (items.length === 0) {
|
|
81
|
-
return [{ Index: 0, Title: 'No favorites found. Make sure sidebar is visible and you have favorites.', Icon: '⚠️' }];
|
|
82
|
-
}
|
|
83
|
-
return items;
|
|
84
|
-
},
|
|
85
|
-
});
|
package/clis/notion/new.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
2
|
-
export const newCommand = cli({
|
|
3
|
-
site: 'notion',
|
|
4
|
-
name: 'new',
|
|
5
|
-
access: 'write',
|
|
6
|
-
description: 'Create a new page in Notion',
|
|
7
|
-
domain: 'localhost',
|
|
8
|
-
strategy: Strategy.UI,
|
|
9
|
-
browser: true,
|
|
10
|
-
args: [
|
|
11
|
-
{ name: 'title', required: false, positional: true, help: 'Page title (optional)' },
|
|
12
|
-
],
|
|
13
|
-
columns: ['Status'],
|
|
14
|
-
func: async (page, kwargs) => {
|
|
15
|
-
const title = kwargs.title;
|
|
16
|
-
// Cmd+N creates a new page in Notion
|
|
17
|
-
const isMac = process.platform === 'darwin';
|
|
18
|
-
await page.pressKey(isMac ? 'Meta+N' : 'Control+N');
|
|
19
|
-
await page.wait(1);
|
|
20
|
-
// If title is provided, type it into the title field
|
|
21
|
-
if (title) {
|
|
22
|
-
await page.evaluate(`
|
|
23
|
-
(function(t) {
|
|
24
|
-
const titleEl = document.querySelector('[placeholder="Untitled"], [data-content-editable-leaf] [placeholder]');
|
|
25
|
-
if (titleEl) {
|
|
26
|
-
titleEl.focus();
|
|
27
|
-
document.execCommand('insertText', false, t);
|
|
28
|
-
}
|
|
29
|
-
})(${JSON.stringify(title)})
|
|
30
|
-
`);
|
|
31
|
-
await page.wait(0.5);
|
|
32
|
-
}
|
|
33
|
-
return [{ Status: title ? `Created page: ${title}` : 'New blank page created' }];
|
|
34
|
-
},
|
|
35
|
-
});
|
package/clis/notion/read.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
2
|
-
export const readCommand = cli({
|
|
3
|
-
site: 'notion',
|
|
4
|
-
name: 'read',
|
|
5
|
-
access: 'read',
|
|
6
|
-
description: 'Read the content of the currently open Notion page',
|
|
7
|
-
domain: 'localhost',
|
|
8
|
-
strategy: Strategy.UI,
|
|
9
|
-
browser: true,
|
|
10
|
-
args: [],
|
|
11
|
-
columns: ['Title', 'Content'],
|
|
12
|
-
func: async (page) => {
|
|
13
|
-
const result = await page.evaluate(`
|
|
14
|
-
(function() {
|
|
15
|
-
// Get the page title
|
|
16
|
-
const titleEl = document.querySelector('[data-block-id] [placeholder="Untitled"], .notion-page-block .notranslate, h1.notion-title, [class*="title"]');
|
|
17
|
-
const title = titleEl ? (titleEl.textContent || '').trim() : document.title;
|
|
18
|
-
|
|
19
|
-
// Get the page content — Notion renders blocks in a frame
|
|
20
|
-
const frame = document.querySelector('.notion-page-content, [class*="page-content"], .layout-content, main');
|
|
21
|
-
const content = frame ? (frame.innerText || frame.textContent || '').trim() : '';
|
|
22
|
-
|
|
23
|
-
return { title, content };
|
|
24
|
-
})()
|
|
25
|
-
`);
|
|
26
|
-
return [{
|
|
27
|
-
Title: result.title || 'Untitled',
|
|
28
|
-
Content: result.content || '(empty page)',
|
|
29
|
-
}];
|
|
30
|
-
},
|
|
31
|
-
});
|
package/clis/notion/search.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
2
|
-
export const searchCommand = cli({
|
|
3
|
-
site: 'notion',
|
|
4
|
-
name: 'search',
|
|
5
|
-
access: 'read',
|
|
6
|
-
description: 'Search pages and databases in Notion via Quick Find (Cmd+P)',
|
|
7
|
-
domain: 'localhost',
|
|
8
|
-
strategy: Strategy.UI,
|
|
9
|
-
browser: true,
|
|
10
|
-
args: [{ name: 'query', required: true, positional: true, help: 'Search query' }],
|
|
11
|
-
columns: ['Index', 'Title'],
|
|
12
|
-
func: async (page, kwargs) => {
|
|
13
|
-
const query = kwargs.query;
|
|
14
|
-
// Open Quick Find
|
|
15
|
-
const isMac = process.platform === 'darwin';
|
|
16
|
-
await page.pressKey(isMac ? 'Meta+P' : 'Control+P');
|
|
17
|
-
await page.wait(0.5);
|
|
18
|
-
// Type the search query
|
|
19
|
-
await page.evaluate(`
|
|
20
|
-
(function(q) {
|
|
21
|
-
const input = document.querySelector('input[placeholder*="Search"], input[type="text"]');
|
|
22
|
-
if (input) {
|
|
23
|
-
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
|
|
24
|
-
setter.call(input, q);
|
|
25
|
-
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
26
|
-
}
|
|
27
|
-
})(${JSON.stringify(query)})
|
|
28
|
-
`);
|
|
29
|
-
await page.wait(1.5);
|
|
30
|
-
// Scrape results
|
|
31
|
-
const results = await page.evaluate(`
|
|
32
|
-
(function() {
|
|
33
|
-
const items = document.querySelectorAll('[role="option"], [class*="searchResult"], [class*="quick-find"] [role="button"]');
|
|
34
|
-
return Array.from(items).slice(0, 20).map((item, i) => ({
|
|
35
|
-
Index: i + 1,
|
|
36
|
-
Title: (item.textContent || '').trim().substring(0, 120),
|
|
37
|
-
}));
|
|
38
|
-
})()
|
|
39
|
-
`);
|
|
40
|
-
// Close Quick Find
|
|
41
|
-
await page.pressKey('Escape');
|
|
42
|
-
if (results.length === 0) {
|
|
43
|
-
return [{ Index: 0, Title: `No results for "${query}"` }];
|
|
44
|
-
}
|
|
45
|
-
return results;
|
|
46
|
-
},
|
|
47
|
-
});
|
package/clis/notion/sidebar.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
2
|
-
export const sidebarCommand = cli({
|
|
3
|
-
site: 'notion',
|
|
4
|
-
name: 'sidebar',
|
|
5
|
-
access: 'read',
|
|
6
|
-
description: 'List pages and databases from the Notion sidebar',
|
|
7
|
-
domain: 'localhost',
|
|
8
|
-
strategy: Strategy.UI,
|
|
9
|
-
browser: true,
|
|
10
|
-
args: [],
|
|
11
|
-
columns: ['Index', 'Title'],
|
|
12
|
-
func: async (page) => {
|
|
13
|
-
const items = await page.evaluate(`
|
|
14
|
-
(function() {
|
|
15
|
-
const results = [];
|
|
16
|
-
// Notion sidebar items
|
|
17
|
-
const selectors = [
|
|
18
|
-
'[class*="sidebar"] [role="treeitem"]',
|
|
19
|
-
'[class*="sidebar"] a',
|
|
20
|
-
'.notion-sidebar [role="button"]',
|
|
21
|
-
'nav [role="treeitem"]',
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
for (const sel of selectors) {
|
|
25
|
-
const nodes = document.querySelectorAll(sel);
|
|
26
|
-
if (nodes.length > 0) {
|
|
27
|
-
nodes.forEach((n, i) => {
|
|
28
|
-
const text = (n.textContent || '').trim().substring(0, 100);
|
|
29
|
-
if (text && text.length > 1) results.push({ Index: i + 1, Title: text });
|
|
30
|
-
});
|
|
31
|
-
break;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return results;
|
|
35
|
-
})()
|
|
36
|
-
`);
|
|
37
|
-
if (items.length === 0) {
|
|
38
|
-
return [{ Index: 0, Title: 'No sidebar items found. Toggle the sidebar first.' }];
|
|
39
|
-
}
|
|
40
|
-
return items;
|
|
41
|
-
},
|
|
42
|
-
});
|
package/clis/notion/status.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
2
|
-
export const statusCommand = cli({
|
|
3
|
-
site: 'notion',
|
|
4
|
-
name: 'status',
|
|
5
|
-
access: 'read',
|
|
6
|
-
description: 'Check active CDP connection to Notion Desktop',
|
|
7
|
-
domain: 'localhost',
|
|
8
|
-
strategy: Strategy.UI,
|
|
9
|
-
browser: true,
|
|
10
|
-
args: [],
|
|
11
|
-
columns: ['Status', 'Url', 'Title'],
|
|
12
|
-
func: async (page) => {
|
|
13
|
-
const url = await page.evaluate('window.location.href');
|
|
14
|
-
const title = await page.evaluate('document.title');
|
|
15
|
-
return [{ Status: 'Connected', Url: url, Title: title }];
|
|
16
|
-
},
|
|
17
|
-
});
|
package/clis/notion/write.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
2
|
-
export const writeCommand = cli({
|
|
3
|
-
site: 'notion',
|
|
4
|
-
name: 'write',
|
|
5
|
-
access: 'write',
|
|
6
|
-
description: 'Append text content to the currently open Notion page',
|
|
7
|
-
domain: 'localhost',
|
|
8
|
-
strategy: Strategy.UI,
|
|
9
|
-
browser: true,
|
|
10
|
-
args: [{ name: 'text', required: true, positional: true, help: 'Text to append to the page' }],
|
|
11
|
-
columns: ['Status'],
|
|
12
|
-
func: async (page, kwargs) => {
|
|
13
|
-
const text = kwargs.text;
|
|
14
|
-
// Focus the page body and move to the end
|
|
15
|
-
await page.evaluate(`
|
|
16
|
-
(function(text) {
|
|
17
|
-
// Find the editable area in Notion
|
|
18
|
-
const editables = document.querySelectorAll('.notion-page-content [contenteditable="true"], [class*="page-content"] [contenteditable="true"]');
|
|
19
|
-
let target = editables.length > 0 ? editables[editables.length - 1] : null;
|
|
20
|
-
|
|
21
|
-
if (!target) {
|
|
22
|
-
// Fallback: just find any contenteditable
|
|
23
|
-
const all = document.querySelectorAll('[contenteditable="true"]');
|
|
24
|
-
target = all.length > 0 ? all[all.length - 1] : null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (!target) throw new Error('Could not find editable area in Notion page');
|
|
28
|
-
|
|
29
|
-
target.focus();
|
|
30
|
-
// Move to end
|
|
31
|
-
const sel = window.getSelection();
|
|
32
|
-
sel.selectAllChildren(target);
|
|
33
|
-
sel.collapseToEnd();
|
|
34
|
-
|
|
35
|
-
document.execCommand('insertText', false, text);
|
|
36
|
-
})(${JSON.stringify(text)})
|
|
37
|
-
`);
|
|
38
|
-
await page.wait(0.5);
|
|
39
|
-
return [{ Status: 'Text appended successfully' }];
|
|
40
|
-
},
|
|
41
|
-
});
|