pinokiod 3.181.0 → 3.182.0
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/kernel/util.js +15 -2
- package/package.json +1 -1
- package/server/index.js +111 -12
- package/server/public/common.js +433 -240
- package/server/public/files-app/app.css +64 -0
- package/server/public/files-app/app.js +87 -0
- package/server/public/install.js +8 -1
- package/server/public/layout.js +11 -1
- package/server/public/sound/beep.mp3 +0 -0
- package/server/public/sound/bell.mp3 +0 -0
- package/server/public/sound/bright-ring.mp3 +0 -0
- package/server/public/sound/clap.mp3 +0 -0
- package/server/public/sound/deep-ring.mp3 +0 -0
- package/server/public/sound/gasp.mp3 +0 -0
- package/server/public/sound/hehe.mp3 +0 -0
- package/server/public/sound/levelup.mp3 +0 -0
- package/server/public/sound/light-pop.mp3 +0 -0
- package/server/public/sound/light-ring.mp3 +0 -0
- package/server/public/sound/meow.mp3 +0 -0
- package/server/public/sound/piano.mp3 +0 -0
- package/server/public/sound/pop.mp3 +0 -0
- package/server/public/sound/uhoh.mp3 +0 -0
- package/server/public/sound/whistle.mp3 +0 -0
- package/server/public/style.css +173 -2
- package/server/public/tab-idle-notifier.js +697 -4
- package/server/public/terminal-settings.js +1131 -0
- package/server/public/urldropdown.css +28 -1
- package/server/views/{terminals.ejs → agents.ejs} +97 -30
- package/server/views/app.ejs +112 -65
- package/server/views/bootstrap.ejs +8 -0
- package/server/views/connect.ejs +1 -1
- package/server/views/d.ejs +172 -18
- package/server/views/editor.ejs +8 -0
- package/server/views/file_browser.ejs +4 -0
- package/server/views/index.ejs +1 -1
- package/server/views/init/index.ejs +9 -1
- package/server/views/install.ejs +8 -0
- package/server/views/net.ejs +1 -1
- package/server/views/network.ejs +1 -1
- package/server/views/pro.ejs +8 -0
- package/server/views/prototype/index.ejs +8 -0
- package/server/views/screenshots.ejs +1 -2
- package/server/views/settings.ejs +1 -2
- package/server/views/shell.ejs +8 -0
- package/server/views/terminal.ejs +8 -0
- package/server/views/tools.ejs +1 -2
package/server/public/common.js
CHANGED
|
@@ -1720,8 +1720,10 @@ if (typeof hotkeys === 'function') {
|
|
|
1720
1720
|
}
|
|
1721
1721
|
};
|
|
1722
1722
|
|
|
1723
|
+
const isFalseyString = (value) => typeof value === 'string' && ['false', '0', 'no', 'off'].includes(value.trim().toLowerCase());
|
|
1724
|
+
|
|
1723
1725
|
const enqueueSound = (url) => {
|
|
1724
|
-
if (!url) {
|
|
1726
|
+
if (!url || url === false || isFalseyString(url)) {
|
|
1725
1727
|
return;
|
|
1726
1728
|
}
|
|
1727
1729
|
pendingSounds.push(url);
|
|
@@ -2533,6 +2535,120 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
2533
2535
|
|
|
2534
2536
|
let createLauncherModalInstance = null;
|
|
2535
2537
|
let createLauncherKeydownHandler = null;
|
|
2538
|
+
let createLauncherModalPromise = null;
|
|
2539
|
+
|
|
2540
|
+
const createLauncherFallbackTools = [
|
|
2541
|
+
{
|
|
2542
|
+
value: 'claude',
|
|
2543
|
+
label: 'Claude Code',
|
|
2544
|
+
iconSrc: '/asset/plugin/code/claude/claude.png',
|
|
2545
|
+
isDefault: true,
|
|
2546
|
+
href: '/run/plugin/code/claude/pinokio.js',
|
|
2547
|
+
category: 'CLI',
|
|
2548
|
+
},
|
|
2549
|
+
{
|
|
2550
|
+
value: 'codex',
|
|
2551
|
+
label: 'OpenAI Codex',
|
|
2552
|
+
iconSrc: '/asset/plugin/code/codex/openai.webp',
|
|
2553
|
+
isDefault: false,
|
|
2554
|
+
href: '/run/plugin/code/codex/pinokio.js',
|
|
2555
|
+
category: 'CLI',
|
|
2556
|
+
},
|
|
2557
|
+
{
|
|
2558
|
+
value: 'gemini',
|
|
2559
|
+
label: 'Google Gemini CLI',
|
|
2560
|
+
iconSrc: '/asset/plugin/code/gemini/gemini.jpeg',
|
|
2561
|
+
isDefault: false,
|
|
2562
|
+
href: '/run/plugin/code/gemini/pinokio.js',
|
|
2563
|
+
category: 'CLI',
|
|
2564
|
+
},
|
|
2565
|
+
];
|
|
2566
|
+
|
|
2567
|
+
let cachedCreateLauncherTools = null;
|
|
2568
|
+
let loadingCreateLauncherTools = null;
|
|
2569
|
+
|
|
2570
|
+
function mapPluginMenuToCreateLauncherTools(menu) {
|
|
2571
|
+
if (!Array.isArray(menu)) return [];
|
|
2572
|
+
|
|
2573
|
+
return menu
|
|
2574
|
+
.map((plugin) => {
|
|
2575
|
+
if (!plugin || (!plugin.href && !plugin.link)) {
|
|
2576
|
+
return null;
|
|
2577
|
+
}
|
|
2578
|
+
const href = typeof plugin.href === 'string' ? plugin.href.trim() : '';
|
|
2579
|
+
const label = plugin.title || plugin.text || plugin.name || href || '';
|
|
2580
|
+
|
|
2581
|
+
let slug = '';
|
|
2582
|
+
if (href) {
|
|
2583
|
+
const segments = href.split('/').filter(Boolean);
|
|
2584
|
+
if (segments.length >= 2) {
|
|
2585
|
+
slug = segments[segments.length - 2] || '';
|
|
2586
|
+
}
|
|
2587
|
+
if (!slug && segments.length) {
|
|
2588
|
+
slug = segments[segments.length - 1] || '';
|
|
2589
|
+
}
|
|
2590
|
+
if (slug.endsWith('.js')) {
|
|
2591
|
+
slug = slug.replace(/\.js$/i, '');
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
if (!slug && label) {
|
|
2595
|
+
slug = label
|
|
2596
|
+
.toLowerCase()
|
|
2597
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
2598
|
+
.replace(/^-+|-+$/g, '');
|
|
2599
|
+
}
|
|
2600
|
+
const value = slug || href || (typeof plugin.link === 'string' ? plugin.link.trim() : '');
|
|
2601
|
+
if (!value) {
|
|
2602
|
+
return null;
|
|
2603
|
+
}
|
|
2604
|
+
const iconSrc = plugin.image || null;
|
|
2605
|
+
const runs = Array.isArray(plugin.run) ? plugin.run : [];
|
|
2606
|
+
const hasExec = runs.some((step) => step && step.method === 'exec');
|
|
2607
|
+
const category = hasExec ? 'IDE' : 'CLI';
|
|
2608
|
+
return {
|
|
2609
|
+
value,
|
|
2610
|
+
label,
|
|
2611
|
+
iconSrc,
|
|
2612
|
+
isDefault: Boolean(plugin.default === true),
|
|
2613
|
+
href: href || null,
|
|
2614
|
+
category,
|
|
2615
|
+
};
|
|
2616
|
+
})
|
|
2617
|
+
.filter(Boolean);
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
async function getCreateLauncherTools() {
|
|
2621
|
+
if (Array.isArray(cachedCreateLauncherTools) && cachedCreateLauncherTools.length > 0) {
|
|
2622
|
+
return cachedCreateLauncherTools;
|
|
2623
|
+
}
|
|
2624
|
+
if (loadingCreateLauncherTools) {
|
|
2625
|
+
return loadingCreateLauncherTools;
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
loadingCreateLauncherTools = fetch('/api/plugin/menu')
|
|
2629
|
+
.then((res) => {
|
|
2630
|
+
if (!res.ok) {
|
|
2631
|
+
throw new Error(`Failed to load plugin menu: ${res.status}`);
|
|
2632
|
+
}
|
|
2633
|
+
return res.json();
|
|
2634
|
+
})
|
|
2635
|
+
.then((data) => {
|
|
2636
|
+
const menu = data && Array.isArray(data.menu) ? data.menu : [];
|
|
2637
|
+
const tools = mapPluginMenuToCreateLauncherTools(menu);
|
|
2638
|
+
return tools.length > 0 ? tools : createLauncherFallbackTools.slice();
|
|
2639
|
+
})
|
|
2640
|
+
.catch((error) => {
|
|
2641
|
+
console.warn('Falling back to default agents for create launcher modal', error);
|
|
2642
|
+
return createLauncherFallbackTools.slice();
|
|
2643
|
+
})
|
|
2644
|
+
.finally(() => {
|
|
2645
|
+
loadingCreateLauncherTools = null;
|
|
2646
|
+
});
|
|
2647
|
+
|
|
2648
|
+
const tools = await loadingCreateLauncherTools;
|
|
2649
|
+
cachedCreateLauncherTools = tools;
|
|
2650
|
+
return tools;
|
|
2651
|
+
}
|
|
2536
2652
|
|
|
2537
2653
|
function initCreateLauncherFlow() {
|
|
2538
2654
|
const trigger = document.getElementById('create-launcher-button');
|
|
@@ -2549,295 +2665,362 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
2549
2665
|
requestAnimationFrame(openPendingCreateLauncherModal);
|
|
2550
2666
|
}
|
|
2551
2667
|
|
|
2552
|
-
function ensureCreateLauncherModal() {
|
|
2668
|
+
async function ensureCreateLauncherModal() {
|
|
2553
2669
|
if (createLauncherModalInstance) {
|
|
2554
2670
|
return createLauncherModalInstance;
|
|
2555
2671
|
}
|
|
2672
|
+
if (createLauncherModalPromise) {
|
|
2673
|
+
return createLauncherModalPromise;
|
|
2674
|
+
}
|
|
2556
2675
|
|
|
2557
|
-
|
|
2558
|
-
|
|
2676
|
+
createLauncherModalPromise = (async () => {
|
|
2677
|
+
const tools = await getCreateLauncherTools();
|
|
2559
2678
|
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
modal.setAttribute('role', 'dialog');
|
|
2563
|
-
modal.setAttribute('aria-modal', 'true');
|
|
2679
|
+
const overlay = document.createElement('div');
|
|
2680
|
+
overlay.className = 'modal-overlay create-launcher-modal-overlay';
|
|
2564
2681
|
|
|
2565
|
-
|
|
2566
|
-
|
|
2682
|
+
const modal = document.createElement('div');
|
|
2683
|
+
modal.className = 'create-launcher-modal';
|
|
2684
|
+
modal.setAttribute('role', 'dialog');
|
|
2685
|
+
modal.setAttribute('aria-modal', 'true');
|
|
2567
2686
|
|
|
2568
|
-
|
|
2569
|
-
|
|
2687
|
+
const header = document.createElement('div');
|
|
2688
|
+
header.className = 'create-launcher-modal-header';
|
|
2570
2689
|
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
headerIcon.className = 'fa-solid fa-wand-magic-sparkles'
|
|
2574
|
-
iconWrapper.appendChild(headerIcon);
|
|
2690
|
+
const iconWrapper = document.createElement('div');
|
|
2691
|
+
iconWrapper.className = 'create-launcher-modal-icon';
|
|
2575
2692
|
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
const title = document.createElement('h3');
|
|
2580
|
-
title.id = 'create-launcher-modal-title';
|
|
2581
|
-
title.textContent = 'Create';
|
|
2693
|
+
const headerIcon = document.createElement('i');
|
|
2694
|
+
headerIcon.className = 'fa-solid fa-wand-magic-sparkles'
|
|
2695
|
+
iconWrapper.appendChild(headerIcon);
|
|
2582
2696
|
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
description.id = 'create-launcher-modal-description';
|
|
2586
|
-
description.textContent = 'Create a reusable and shareable launcher for any task or any app'
|
|
2697
|
+
const headingStack = document.createElement('div');
|
|
2698
|
+
headingStack.className = 'create-launcher-modal-headings';
|
|
2587
2699
|
|
|
2588
|
-
|
|
2589
|
-
|
|
2700
|
+
const title = document.createElement('h3');
|
|
2701
|
+
title.id = 'create-launcher-modal-title';
|
|
2702
|
+
title.textContent = 'Create';
|
|
2590
2703
|
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2704
|
+
const description = document.createElement('p');
|
|
2705
|
+
description.className = 'create-launcher-modal-description';
|
|
2706
|
+
description.id = 'create-launcher-modal-description';
|
|
2707
|
+
description.textContent = 'Create a reusable and shareable launcher for any task or any app'
|
|
2595
2708
|
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
promptLabel.textContent = 'What do you want to do?';
|
|
2709
|
+
modal.setAttribute('aria-labelledby', title.id);
|
|
2710
|
+
modal.setAttribute('aria-describedby', description.id);
|
|
2599
2711
|
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2712
|
+
headingStack.appendChild(title);
|
|
2713
|
+
headingStack.appendChild(description);
|
|
2714
|
+
header.appendChild(iconWrapper);
|
|
2715
|
+
header.appendChild(headingStack);
|
|
2604
2716
|
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2717
|
+
const promptLabel = document.createElement('label');
|
|
2718
|
+
promptLabel.className = 'create-launcher-modal-label';
|
|
2719
|
+
promptLabel.textContent = 'What do you want to do?';
|
|
2608
2720
|
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2721
|
+
const promptTextarea = document.createElement('textarea');
|
|
2722
|
+
promptTextarea.className = 'create-launcher-modal-textarea';
|
|
2723
|
+
promptTextarea.placeholder = 'Examples: "a 1-click launcher for ComfyUI", "I want to change file format", "I want to clone a website to run locally", etc. (Leave empty to decide later)';
|
|
2724
|
+
promptLabel.appendChild(promptTextarea);
|
|
2612
2725
|
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2726
|
+
const templateWrapper = document.createElement('div');
|
|
2727
|
+
templateWrapper.className = 'create-launcher-modal-template';
|
|
2728
|
+
templateWrapper.style.display = 'none';
|
|
2616
2729
|
|
|
2617
|
-
|
|
2618
|
-
|
|
2730
|
+
const templateTitle = document.createElement('div');
|
|
2731
|
+
templateTitle.className = 'create-launcher-modal-template-title';
|
|
2732
|
+
templateTitle.textContent = 'Template variables';
|
|
2619
2733
|
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2734
|
+
const templateDescription = document.createElement('p');
|
|
2735
|
+
templateDescription.className = 'create-launcher-modal-template-description';
|
|
2736
|
+
templateDescription.textContent = 'Fill in each variable below before creating your launcher.';
|
|
2623
2737
|
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
folderLabel.textContent = 'name';
|
|
2738
|
+
const templateFields = document.createElement('div');
|
|
2739
|
+
templateFields.className = 'create-launcher-modal-template-fields';
|
|
2627
2740
|
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
folderInput.className = 'create-launcher-modal-input';
|
|
2632
|
-
folderLabel.appendChild(folderInput);
|
|
2741
|
+
templateWrapper.appendChild(templateTitle);
|
|
2742
|
+
templateWrapper.appendChild(templateDescription);
|
|
2743
|
+
templateWrapper.appendChild(templateFields);
|
|
2633
2744
|
|
|
2745
|
+
const folderLabel = document.createElement('label');
|
|
2746
|
+
folderLabel.className = 'create-launcher-modal-label';
|
|
2747
|
+
folderLabel.textContent = 'name';
|
|
2634
2748
|
|
|
2635
|
-
|
|
2636
|
-
|
|
2749
|
+
const folderInput = document.createElement('input');
|
|
2750
|
+
folderInput.type = 'text';
|
|
2751
|
+
folderInput.placeholder = 'example: my-launcher';
|
|
2752
|
+
folderInput.className = 'create-launcher-modal-input';
|
|
2753
|
+
folderLabel.appendChild(folderInput);
|
|
2637
2754
|
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
toolTitle.textContent = 'Choose AI tool';
|
|
2755
|
+
const toolWrapper = document.createElement('div');
|
|
2756
|
+
toolWrapper.className = 'create-launcher-modal-tools';
|
|
2641
2757
|
|
|
2642
|
-
|
|
2643
|
-
|
|
2758
|
+
const toolTitle = document.createElement('div');
|
|
2759
|
+
toolTitle.className = 'create-launcher-modal-tools-title';
|
|
2760
|
+
toolTitle.textContent = 'Select Agent';
|
|
2644
2761
|
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
{ value: 'codex', label: 'OpenAI Codex', iconSrc: '/asset/plugin/code/codex/openai.webp', defaultChecked: false },
|
|
2648
|
-
{ value: 'gemini', label: 'Google Gemini CLI', iconSrc: '/asset/plugin/code/gemini/gemini.jpeg', defaultChecked: false }
|
|
2649
|
-
];
|
|
2762
|
+
const toolOptions = document.createElement('div');
|
|
2763
|
+
toolOptions.className = 'create-launcher-modal-tools-options';
|
|
2650
2764
|
|
|
2651
|
-
|
|
2765
|
+
const toolEntries = [];
|
|
2766
|
+
const defaultToolIndex = tools.findIndex((tool) => tool.isDefault);
|
|
2767
|
+
const initialSelectionIndex = defaultToolIndex >= 0 ? defaultToolIndex : (tools.length > 0 ? 0 : -1);
|
|
2652
2768
|
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2769
|
+
const groupedTools = tools.reduce((acc, tool, index) => {
|
|
2770
|
+
const category = tool.category || 'CLI';
|
|
2771
|
+
if (!acc.has(category)) {
|
|
2772
|
+
acc.set(category, []);
|
|
2773
|
+
}
|
|
2774
|
+
acc.get(category).push({ tool, index });
|
|
2775
|
+
return acc;
|
|
2776
|
+
}, new Map());
|
|
2777
|
+
|
|
2778
|
+
const categoryOrder = ['CLI', 'IDE'];
|
|
2779
|
+
const orderedGroups = [];
|
|
2780
|
+
categoryOrder.forEach((cat) => {
|
|
2781
|
+
if (groupedTools.has(cat)) {
|
|
2782
|
+
orderedGroups.push([cat, groupedTools.get(cat)]);
|
|
2783
|
+
groupedTools.delete(cat);
|
|
2784
|
+
}
|
|
2785
|
+
});
|
|
2786
|
+
groupedTools.forEach((value, key) => {
|
|
2787
|
+
orderedGroups.push([key, value]);
|
|
2788
|
+
});
|
|
2656
2789
|
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
radio.value = value;
|
|
2661
|
-
if (defaultChecked) {
|
|
2662
|
-
radio.checked = true;
|
|
2663
|
-
}
|
|
2790
|
+
orderedGroups.forEach(([category, entries]) => {
|
|
2791
|
+
const group = document.createElement('div');
|
|
2792
|
+
group.className = 'create-launcher-modal-tools-group';
|
|
2664
2793
|
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
option.appendChild(radio);
|
|
2670
|
-
if (iconSrc) {
|
|
2671
|
-
const icon = document.createElement('img');
|
|
2672
|
-
icon.className = 'create-launcher-modal-tool-icon';
|
|
2673
|
-
icon.src = iconSrc;
|
|
2674
|
-
icon.alt = `${label} icon`;
|
|
2675
|
-
icon.onerror = () => { icon.style.display='none'; }
|
|
2676
|
-
option.appendChild(icon);
|
|
2677
|
-
}
|
|
2678
|
-
option.appendChild(badge);
|
|
2679
|
-
toolOptions.appendChild(option);
|
|
2680
|
-
toolEntries.push({ input: radio, container: option });
|
|
2681
|
-
radio.addEventListener('change', () => {
|
|
2682
|
-
updateToolSelections(toolEntries);
|
|
2683
|
-
});
|
|
2684
|
-
});
|
|
2794
|
+
const heading = document.createElement('div');
|
|
2795
|
+
heading.className = 'create-launcher-modal-tools-group-title';
|
|
2796
|
+
heading.textContent = category;
|
|
2797
|
+
group.appendChild(heading);
|
|
2685
2798
|
|
|
2686
|
-
|
|
2687
|
-
|
|
2799
|
+
const groupList = document.createElement('div');
|
|
2800
|
+
groupList.className = 'create-launcher-modal-tools-group-options';
|
|
2688
2801
|
|
|
2689
|
-
|
|
2690
|
-
|
|
2802
|
+
const sortedEntries = entries.slice().sort((a, b) => {
|
|
2803
|
+
const nameA = (a.tool && a.tool.label ? a.tool.label : '').toLowerCase();
|
|
2804
|
+
const nameB = (b.tool && b.tool.label ? b.tool.label : '').toLowerCase();
|
|
2805
|
+
if (nameA < nameB) return -1;
|
|
2806
|
+
if (nameA > nameB) return 1;
|
|
2807
|
+
return 0;
|
|
2808
|
+
});
|
|
2691
2809
|
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
actions.appendChild(cancelButton);
|
|
2706
|
-
actions.appendChild(confirmButton);
|
|
2707
|
-
|
|
2708
|
-
const advancedLink = document.createElement('a');
|
|
2709
|
-
advancedLink.className = 'create-launcher-modal-advanced';
|
|
2710
|
-
advancedLink.href = '/init';
|
|
2711
|
-
advancedLink.textContent = 'Or, try advanced options';
|
|
2712
|
-
|
|
2713
|
-
const bookmarkletLink = document.createElement('a');
|
|
2714
|
-
bookmarkletLink.className = 'create-launcher-modal-advanced secondary';
|
|
2715
|
-
bookmarkletLink.href = '/bookmarklet';
|
|
2716
|
-
bookmarkletLink.target = '_blank';
|
|
2717
|
-
bookmarkletLink.setAttribute("features", "browser")
|
|
2718
|
-
bookmarkletLink.rel = 'noopener';
|
|
2719
|
-
bookmarkletLink.textContent = 'Add 1-click bookmarklet';
|
|
2720
|
-
|
|
2721
|
-
const linkRow = document.createElement('div');
|
|
2722
|
-
linkRow.className = 'create-launcher-modal-links';
|
|
2723
|
-
linkRow.appendChild(advancedLink);
|
|
2724
|
-
linkRow.appendChild(bookmarkletLink);
|
|
2725
|
-
|
|
2726
|
-
modal.appendChild(header);
|
|
2727
|
-
modal.appendChild(promptLabel);
|
|
2728
|
-
modal.appendChild(templateWrapper);
|
|
2729
|
-
modal.appendChild(folderLabel);
|
|
2730
|
-
modal.appendChild(toolWrapper);
|
|
2731
|
-
modal.appendChild(error);
|
|
2732
|
-
modal.appendChild(actions);
|
|
2733
|
-
modal.appendChild(linkRow);
|
|
2734
|
-
overlay.appendChild(modal);
|
|
2735
|
-
document.body.appendChild(overlay);
|
|
2810
|
+
sortedEntries.forEach(({ tool, index }) => {
|
|
2811
|
+
const option = document.createElement('label');
|
|
2812
|
+
option.className = 'create-launcher-modal-tool';
|
|
2813
|
+
|
|
2814
|
+
const radio = document.createElement('input');
|
|
2815
|
+
radio.type = 'radio';
|
|
2816
|
+
radio.name = 'create-launcher-tool';
|
|
2817
|
+
radio.value = tool.value;
|
|
2818
|
+
radio.dataset.agentLabel = tool.label;
|
|
2819
|
+
radio.dataset.agentCategory = category;
|
|
2820
|
+
if (tool.href) {
|
|
2821
|
+
radio.dataset.agentHref = tool.href;
|
|
2822
|
+
}
|
|
2736
2823
|
|
|
2737
|
-
|
|
2738
|
-
|
|
2824
|
+
if (index === initialSelectionIndex) {
|
|
2825
|
+
radio.checked = true;
|
|
2826
|
+
}
|
|
2739
2827
|
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2828
|
+
const badge = document.createElement('span');
|
|
2829
|
+
badge.className = 'create-launcher-modal-tool-label';
|
|
2830
|
+
badge.textContent = tool.label;
|
|
2831
|
+
|
|
2832
|
+
option.appendChild(radio);
|
|
2833
|
+
if (tool.iconSrc) {
|
|
2834
|
+
const icon = document.createElement('img');
|
|
2835
|
+
icon.className = 'create-launcher-modal-tool-icon';
|
|
2836
|
+
icon.src = tool.iconSrc;
|
|
2837
|
+
icon.alt = `${tool.label} icon`;
|
|
2838
|
+
icon.onerror = () => { icon.style.display = 'none'; };
|
|
2839
|
+
option.appendChild(icon);
|
|
2840
|
+
}
|
|
2841
|
+
option.appendChild(badge);
|
|
2842
|
+
groupList.appendChild(option);
|
|
2843
|
+
const entry = { input: radio, container: option, meta: tool };
|
|
2844
|
+
toolEntries.push(entry);
|
|
2845
|
+
radio.addEventListener('change', () => {
|
|
2846
|
+
updateToolSelections(toolEntries);
|
|
2847
|
+
});
|
|
2848
|
+
});
|
|
2744
2849
|
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
newValues.set(name, defaults[name]);
|
|
2748
|
-
} else if (previousValues.has(name)) {
|
|
2749
|
-
newValues.set(name, previousValues.get(name));
|
|
2750
|
-
} else {
|
|
2751
|
-
newValues.set(name, '');
|
|
2752
|
-
}
|
|
2850
|
+
group.appendChild(groupList);
|
|
2851
|
+
toolOptions.appendChild(group);
|
|
2753
2852
|
});
|
|
2754
2853
|
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
return;
|
|
2854
|
+
if (!toolEntries.length) {
|
|
2855
|
+
const emptyState = document.createElement('div');
|
|
2856
|
+
emptyState.className = 'create-launcher-modal-tools-empty';
|
|
2857
|
+
emptyState.textContent = 'No agents available.';
|
|
2858
|
+
toolOptions.appendChild(emptyState);
|
|
2761
2859
|
}
|
|
2762
2860
|
|
|
2763
|
-
|
|
2861
|
+
toolWrapper.appendChild(toolTitle);
|
|
2862
|
+
toolWrapper.appendChild(toolOptions);
|
|
2863
|
+
|
|
2864
|
+
const error = document.createElement('div');
|
|
2865
|
+
error.className = 'create-launcher-modal-error';
|
|
2866
|
+
|
|
2867
|
+
const actions = document.createElement('div');
|
|
2868
|
+
actions.className = 'create-launcher-modal-actions';
|
|
2869
|
+
|
|
2870
|
+
const cancelButton = document.createElement('button');
|
|
2871
|
+
cancelButton.type = 'button';
|
|
2872
|
+
cancelButton.className = 'create-launcher-modal-button cancel';
|
|
2873
|
+
cancelButton.textContent = 'Cancel';
|
|
2874
|
+
|
|
2875
|
+
const confirmButton = document.createElement('button');
|
|
2876
|
+
confirmButton.type = 'button';
|
|
2877
|
+
confirmButton.className = 'create-launcher-modal-button confirm';
|
|
2878
|
+
confirmButton.textContent = 'Create';
|
|
2879
|
+
|
|
2880
|
+
actions.appendChild(cancelButton);
|
|
2881
|
+
actions.appendChild(confirmButton);
|
|
2882
|
+
|
|
2883
|
+
const advancedLink = document.createElement('a');
|
|
2884
|
+
advancedLink.className = 'create-launcher-modal-advanced';
|
|
2885
|
+
advancedLink.href = '/init';
|
|
2886
|
+
advancedLink.textContent = 'Or, try advanced options';
|
|
2887
|
+
|
|
2888
|
+
const bookmarkletLink = document.createElement('a');
|
|
2889
|
+
bookmarkletLink.className = 'create-launcher-modal-advanced secondary';
|
|
2890
|
+
bookmarkletLink.href = '/bookmarklet';
|
|
2891
|
+
bookmarkletLink.target = '_blank';
|
|
2892
|
+
bookmarkletLink.setAttribute('features', 'browser');
|
|
2893
|
+
bookmarkletLink.rel = 'noopener';
|
|
2894
|
+
bookmarkletLink.textContent = 'Add 1-click bookmarklet';
|
|
2895
|
+
|
|
2896
|
+
const linkRow = document.createElement('div');
|
|
2897
|
+
linkRow.className = 'create-launcher-modal-links';
|
|
2898
|
+
linkRow.appendChild(advancedLink);
|
|
2899
|
+
linkRow.appendChild(bookmarkletLink);
|
|
2900
|
+
|
|
2901
|
+
modal.appendChild(header);
|
|
2902
|
+
modal.appendChild(promptLabel);
|
|
2903
|
+
modal.appendChild(templateWrapper);
|
|
2904
|
+
modal.appendChild(folderLabel);
|
|
2905
|
+
modal.appendChild(toolWrapper);
|
|
2906
|
+
modal.appendChild(error);
|
|
2907
|
+
modal.appendChild(actions);
|
|
2908
|
+
modal.appendChild(linkRow);
|
|
2909
|
+
overlay.appendChild(modal);
|
|
2910
|
+
document.body.appendChild(overlay);
|
|
2911
|
+
|
|
2912
|
+
let folderEditedByUser = false;
|
|
2913
|
+
let templateValues = new Map();
|
|
2914
|
+
|
|
2915
|
+
function syncTemplateFields(promptText, defaults = {}) {
|
|
2916
|
+
const variableNames = extractTemplateVariableNames(promptText);
|
|
2917
|
+
const previousValues = templateValues;
|
|
2918
|
+
const newValues = new Map();
|
|
2919
|
+
|
|
2920
|
+
variableNames.forEach((name) => {
|
|
2921
|
+
if (Object.prototype.hasOwnProperty.call(defaults, name) && defaults[name] !== undefined) {
|
|
2922
|
+
newValues.set(name, defaults[name]);
|
|
2923
|
+
} else if (previousValues.has(name)) {
|
|
2924
|
+
newValues.set(name, previousValues.get(name));
|
|
2925
|
+
} else {
|
|
2926
|
+
newValues.set(name, '');
|
|
2927
|
+
}
|
|
2928
|
+
});
|
|
2764
2929
|
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
field.className = 'create-launcher-modal-template-field';
|
|
2930
|
+
templateValues = newValues;
|
|
2931
|
+
templateFields.innerHTML = '';
|
|
2768
2932
|
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2933
|
+
if (variableNames.length === 0) {
|
|
2934
|
+
templateWrapper.style.display = 'none';
|
|
2935
|
+
return;
|
|
2936
|
+
}
|
|
2772
2937
|
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2938
|
+
templateWrapper.style.display = 'flex';
|
|
2939
|
+
|
|
2940
|
+
variableNames.forEach((name) => {
|
|
2941
|
+
const field = document.createElement('label');
|
|
2942
|
+
field.className = 'create-launcher-modal-template-field';
|
|
2943
|
+
|
|
2944
|
+
const labelText = document.createElement('span');
|
|
2945
|
+
labelText.className = 'create-launcher-modal-template-field-label';
|
|
2946
|
+
labelText.textContent = name;
|
|
2947
|
+
|
|
2948
|
+
const input = document.createElement('input');
|
|
2949
|
+
input.type = 'text';
|
|
2950
|
+
input.className = 'create-launcher-modal-template-input';
|
|
2951
|
+
input.placeholder = `Enter ${name}`;
|
|
2952
|
+
input.value = templateValues.get(name) || '';
|
|
2953
|
+
input.dataset.templateInput = name;
|
|
2954
|
+
input.addEventListener('input', () => {
|
|
2955
|
+
templateValues.set(name, input.value);
|
|
2956
|
+
});
|
|
2957
|
+
|
|
2958
|
+
field.appendChild(labelText);
|
|
2959
|
+
field.appendChild(input);
|
|
2960
|
+
templateFields.appendChild(field);
|
|
2781
2961
|
});
|
|
2962
|
+
}
|
|
2782
2963
|
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
templateFields.appendChild(field);
|
|
2964
|
+
folderInput.addEventListener('input', () => {
|
|
2965
|
+
folderEditedByUser = true;
|
|
2786
2966
|
});
|
|
2787
|
-
}
|
|
2788
2967
|
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2968
|
+
promptTextarea.addEventListener('input', () => {
|
|
2969
|
+
syncTemplateFields(promptTextarea.value);
|
|
2970
|
+
if (folderEditedByUser) return;
|
|
2971
|
+
folderInput.value = generateFolderSuggestion(promptTextarea.value);
|
|
2972
|
+
});
|
|
2792
2973
|
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2974
|
+
cancelButton.addEventListener('click', hideCreateLauncherModal);
|
|
2975
|
+
confirmButton.addEventListener('click', submitCreateLauncherModal);
|
|
2976
|
+
overlay.addEventListener('click', (event) => {
|
|
2977
|
+
if (event.target === overlay) {
|
|
2978
|
+
hideCreateLauncherModal();
|
|
2979
|
+
}
|
|
2980
|
+
});
|
|
2798
2981
|
|
|
2799
|
-
|
|
2800
|
-
confirmButton.addEventListener('click', submitCreateLauncherModal);
|
|
2801
|
-
overlay.addEventListener('click', (event) => {
|
|
2802
|
-
if (event.target === overlay) {
|
|
2982
|
+
advancedLink.addEventListener('click', () => {
|
|
2803
2983
|
hideCreateLauncherModal();
|
|
2804
|
-
}
|
|
2805
|
-
});
|
|
2984
|
+
});
|
|
2806
2985
|
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2986
|
+
bookmarkletLink.addEventListener('click', () => {
|
|
2987
|
+
hideCreateLauncherModal();
|
|
2988
|
+
});
|
|
2810
2989
|
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2990
|
+
createLauncherModalInstance = {
|
|
2991
|
+
overlay,
|
|
2992
|
+
modal,
|
|
2993
|
+
folderInput,
|
|
2994
|
+
promptTextarea,
|
|
2995
|
+
cancelButton,
|
|
2996
|
+
confirmButton,
|
|
2997
|
+
error,
|
|
2998
|
+
toolEntries,
|
|
2999
|
+
toolOptions,
|
|
3000
|
+
toolWrapper,
|
|
3001
|
+
resetFolderTracking() {
|
|
3002
|
+
folderEditedByUser = false;
|
|
3003
|
+
},
|
|
3004
|
+
syncTemplateFields,
|
|
3005
|
+
getTemplateValues() {
|
|
3006
|
+
return new Map(templateValues);
|
|
3007
|
+
},
|
|
3008
|
+
templateFields,
|
|
3009
|
+
markFolderEdited() {
|
|
3010
|
+
folderEditedByUser = true;
|
|
3011
|
+
}
|
|
3012
|
+
};
|
|
2814
3013
|
|
|
2815
|
-
|
|
2816
|
-
overlay,
|
|
2817
|
-
modal,
|
|
2818
|
-
folderInput,
|
|
2819
|
-
promptTextarea,
|
|
2820
|
-
cancelButton,
|
|
2821
|
-
confirmButton,
|
|
2822
|
-
error,
|
|
2823
|
-
toolEntries,
|
|
2824
|
-
// description,
|
|
2825
|
-
resetFolderTracking() {
|
|
2826
|
-
folderEditedByUser = false;
|
|
2827
|
-
},
|
|
2828
|
-
syncTemplateFields,
|
|
2829
|
-
getTemplateValues() {
|
|
2830
|
-
return new Map(templateValues);
|
|
2831
|
-
},
|
|
2832
|
-
templateFields,
|
|
2833
|
-
markFolderEdited() {
|
|
2834
|
-
folderEditedByUser = true;
|
|
2835
|
-
}
|
|
2836
|
-
};
|
|
3014
|
+
updateToolSelections(toolEntries);
|
|
2837
3015
|
|
|
2838
|
-
|
|
3016
|
+
return createLauncherModalInstance;
|
|
3017
|
+
})();
|
|
2839
3018
|
|
|
2840
|
-
|
|
3019
|
+
try {
|
|
3020
|
+
return await createLauncherModalPromise;
|
|
3021
|
+
} finally {
|
|
3022
|
+
createLauncherModalPromise = null;
|
|
3023
|
+
}
|
|
2841
3024
|
}
|
|
2842
3025
|
|
|
2843
3026
|
async function showCreateLauncherModal(defaults = {}) {
|
|
@@ -2851,7 +3034,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
2851
3034
|
return
|
|
2852
3035
|
}
|
|
2853
3036
|
|
|
2854
|
-
const modal = ensureCreateLauncherModal();
|
|
3037
|
+
const modal = await ensureCreateLauncherModal();
|
|
2855
3038
|
|
|
2856
3039
|
modal.error.textContent = '';
|
|
2857
3040
|
modal.resetFolderTracking();
|
|
@@ -2870,8 +3053,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
2870
3053
|
}
|
|
2871
3054
|
|
|
2872
3055
|
const matchingToolEntry = modal.toolEntries.find((entry) => entry.input.value === tool);
|
|
3056
|
+
const defaultToolEntryIndex = modal.toolEntries.findIndex((entry) => entry.meta && entry.meta.isDefault);
|
|
3057
|
+
const fallbackToolIndex = defaultToolEntryIndex >= 0 ? defaultToolEntryIndex : 0;
|
|
2873
3058
|
modal.toolEntries.forEach((entry, index) => {
|
|
2874
|
-
entry.input.checked = matchingToolEntry ? entry === matchingToolEntry : index ===
|
|
3059
|
+
entry.input.checked = matchingToolEntry ? entry === matchingToolEntry : index === fallbackToolIndex;
|
|
2875
3060
|
});
|
|
2876
3061
|
updateToolSelections(modal.toolEntries);
|
|
2877
3062
|
|
|
@@ -2907,14 +3092,22 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
2907
3092
|
}
|
|
2908
3093
|
}
|
|
2909
3094
|
|
|
2910
|
-
function submitCreateLauncherModal() {
|
|
2911
|
-
const modal = ensureCreateLauncherModal();
|
|
3095
|
+
async function submitCreateLauncherModal() {
|
|
3096
|
+
const modal = await ensureCreateLauncherModal();
|
|
2912
3097
|
modal.error.textContent = '';
|
|
2913
3098
|
|
|
2914
3099
|
const folderName = modal.folderInput.value.trim();
|
|
2915
3100
|
const rawPrompt = modal.promptTextarea.value;
|
|
2916
3101
|
const templateValues = modal.getTemplateValues ? modal.getTemplateValues() : new Map();
|
|
2917
|
-
const
|
|
3102
|
+
const selectedEntry = modal.toolEntries.find((entry) => entry.input.checked);
|
|
3103
|
+
const defaultToolEntryIndex = modal.toolEntries.findIndex((entry) => entry.meta && entry.meta.isDefault);
|
|
3104
|
+
const fallbackEntry = defaultToolEntryIndex >= 0 ? modal.toolEntries[defaultToolEntryIndex] : modal.toolEntries[0];
|
|
3105
|
+
const selectedTool = (selectedEntry || fallbackEntry)?.input.value || '';
|
|
3106
|
+
|
|
3107
|
+
if (!selectedTool) {
|
|
3108
|
+
modal.error.textContent = 'Please select an agent.';
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
2918
3111
|
|
|
2919
3112
|
if (!folderName) {
|
|
2920
3113
|
modal.error.textContent = 'Please enter a folder name.';
|