pinokiod 3.85.0 → 3.86.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/api/index.js +7 -0
- package/kernel/bin/caddy.js +10 -4
- package/kernel/peer.js +0 -3
- package/kernel/prototype.js +1 -0
- package/kernel/shell.js +43 -2
- package/kernel/util.js +2 -0
- package/package.json +1 -1
- package/pipe/views/login.ejs +1 -1
- package/server/index.js +133 -83
- package/server/public/common.js +534 -0
- package/server/public/opener.js +12 -11
- package/server/public/serve/style.css +1 -1
- package/server/public/style.css +25 -24
- package/server/public/urldropdown.css +473 -4
- package/server/public/urldropdown.js +202 -8
- package/server/views/404.ejs +1 -1
- package/server/views/500.ejs +1 -1
- package/server/views/app.ejs +29 -33
- package/server/views/bookmarklet.ejs +197 -0
- package/server/views/connect/x.ejs +4 -4
- package/server/views/connect.ejs +10 -10
- package/server/views/container.ejs +2 -2
- package/server/views/d.ejs +3 -3
- package/server/views/download.ejs +1 -1
- package/server/views/editor.ejs +1 -1
- package/server/views/env_editor.ejs +3 -3
- package/server/views/explore.ejs +2 -2
- package/server/views/file_explorer.ejs +2 -2
- package/server/views/git.ejs +7 -7
- package/server/views/github.ejs +3 -3
- package/server/views/help.ejs +2 -2
- package/server/views/index.ejs +5 -5
- package/server/views/index2.ejs +3 -3
- package/server/views/init/index.ejs +11 -74
- package/server/views/install.ejs +3 -3
- package/server/views/keys.ejs +2 -2
- package/server/views/mini.ejs +2 -2
- package/server/views/net.ejs +6 -6
- package/server/views/network.ejs +21 -21
- package/server/views/network2.ejs +10 -10
- package/server/views/old_network.ejs +8 -8
- package/server/views/pro.ejs +369 -0
- package/server/views/prototype/index.ejs +2 -2
- package/server/views/required_env_editor.ejs +2 -2
- package/server/views/review.ejs +6 -6
- package/server/views/screenshots.ejs +5 -4
- package/server/views/settings.ejs +3 -3
- package/server/views/setup.ejs +2 -2
- package/server/views/setup_home.ejs +2 -2
- package/server/views/share_editor.ejs +4 -4
- package/server/views/shell.ejs +3 -3
- package/server/views/start.ejs +2 -2
- package/server/views/task.ejs +2 -2
- package/server/views/terminal.ejs +5 -4
- package/server/views/tools.ejs +13 -13
|
@@ -24,6 +24,9 @@ function initUrlDropdown(config = {}) {
|
|
|
24
24
|
let isDropdownVisible = false;
|
|
25
25
|
let allProcesses = []; // Store all processes for filtering
|
|
26
26
|
let filteredProcesses = []; // Store currently filtered processes
|
|
27
|
+
let createLauncherModal = null;
|
|
28
|
+
let pendingCreateDetail = null;
|
|
29
|
+
const EMPTY_STATE_DESCRIPTION = 'enter a prompt to create a launcher';
|
|
27
30
|
|
|
28
31
|
// Initialize input field state based on clear behavior
|
|
29
32
|
initializeInputValue();
|
|
@@ -272,11 +275,7 @@ function initUrlDropdown(config = {}) {
|
|
|
272
275
|
|
|
273
276
|
function populateDropdown(processes) {
|
|
274
277
|
if (processes.length === 0) {
|
|
275
|
-
|
|
276
|
-
const message = query
|
|
277
|
-
? `No processes match "${query}"`
|
|
278
|
-
: 'No running processes found';
|
|
279
|
-
dropdown.innerHTML = `<div class="url-dropdown-empty">${message}</div>`;
|
|
278
|
+
showEmptyState(dropdown, urlInput);
|
|
280
279
|
return;
|
|
281
280
|
}
|
|
282
281
|
|
|
@@ -514,9 +513,7 @@ function initUrlDropdown(config = {}) {
|
|
|
514
513
|
const modalInput = modalDropdown.parentElement.querySelector('.url-modal-input');
|
|
515
514
|
|
|
516
515
|
if (processes.length === 0) {
|
|
517
|
-
|
|
518
|
-
const message = query ? `No processes match "${query}"` : 'No running processes found';
|
|
519
|
-
modalDropdown.innerHTML = `<div class="url-dropdown-empty">${message}</div>`;
|
|
516
|
+
showEmptyState(modalDropdown, modalInput);
|
|
520
517
|
return;
|
|
521
518
|
}
|
|
522
519
|
|
|
@@ -633,6 +630,203 @@ function initUrlDropdown(config = {}) {
|
|
|
633
630
|
filteredProcesses = [];
|
|
634
631
|
}
|
|
635
632
|
};
|
|
633
|
+
function showEmptyState(container, inputElement) {
|
|
634
|
+
container.innerHTML = createEmptyStateHtml(getEmptyStateMessage(inputElement));
|
|
635
|
+
attachCreateButtonHandler(container, inputElement);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function getEmptyStateMessage(inputElement) {
|
|
639
|
+
const rawValue = inputElement.value.trim();
|
|
640
|
+
return rawValue ? `No processes match "${rawValue}"` : 'No running processes found';
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function createEmptyStateHtml(message) {
|
|
644
|
+
return `
|
|
645
|
+
<div class="url-dropdown-empty">
|
|
646
|
+
<div class="url-dropdown-empty-message">${escapeHtml(message)}</div>
|
|
647
|
+
<div class="url-dropdown-empty-actions">
|
|
648
|
+
<button type="button" class="url-dropdown-create-button">Create</button>
|
|
649
|
+
<div class="url-dropdown-empty-description">${escapeHtml(EMPTY_STATE_DESCRIPTION)}</div>
|
|
650
|
+
</div>
|
|
651
|
+
</div>
|
|
652
|
+
`;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function attachCreateButtonHandler(container, inputElement) {
|
|
656
|
+
const createButton = container.querySelector('.url-dropdown-create-button');
|
|
657
|
+
if (!createButton) return;
|
|
658
|
+
|
|
659
|
+
createButton.addEventListener('click', function(event) {
|
|
660
|
+
event.preventDefault();
|
|
661
|
+
event.stopPropagation();
|
|
662
|
+
const prompt = inputElement.value.trim();
|
|
663
|
+
const detail = {
|
|
664
|
+
query: prompt,
|
|
665
|
+
prompt,
|
|
666
|
+
input: inputElement,
|
|
667
|
+
dropdown: container,
|
|
668
|
+
context: inputElement === urlInput ? 'dropdown' : 'modal'
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
if (detail.context === 'dropdown') {
|
|
672
|
+
hideDropdown();
|
|
673
|
+
} else {
|
|
674
|
+
closeMobileModal();
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
showCreateLauncherModal(detail);
|
|
678
|
+
|
|
679
|
+
if (typeof options.onCreate === 'function') {
|
|
680
|
+
options.onCreate(detail);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (typeof CustomEvent === 'function') {
|
|
684
|
+
container.dispatchEvent(new CustomEvent('urlDropdownCreate', { detail }));
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function showCreateLauncherModal(detail) {
|
|
690
|
+
const modal = getCreateLauncherModal();
|
|
691
|
+
pendingCreateDetail = detail;
|
|
692
|
+
|
|
693
|
+
modal.error.textContent = '';
|
|
694
|
+
modal.overlay.style.display = 'flex';
|
|
695
|
+
|
|
696
|
+
// const defaultName = generateFolderName(detail.prompt);
|
|
697
|
+
modal.input.value = "";
|
|
698
|
+
modal.description.textContent = detail.prompt
|
|
699
|
+
? `Prompt: ${detail.prompt}`
|
|
700
|
+
: 'Enter a prompt in the search bar to describe your launcher.';
|
|
701
|
+
modal.input.focus();
|
|
702
|
+
modal.input.select();
|
|
703
|
+
document.addEventListener('keydown', handleCreateModalEscape, true);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
function hideCreateLauncherModal() {
|
|
707
|
+
const modal = createLauncherModal;
|
|
708
|
+
if (!modal) return;
|
|
709
|
+
modal.overlay.style.display = 'none';
|
|
710
|
+
pendingCreateDetail = null;
|
|
711
|
+
document.removeEventListener('keydown', handleCreateModalEscape, true);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function confirmCreateLauncherModal() {
|
|
715
|
+
if (!createLauncherModal || !pendingCreateDetail) return;
|
|
716
|
+
const folderName = createLauncherModal.input.value.trim();
|
|
717
|
+
if (!folderName) {
|
|
718
|
+
createLauncherModal.error.textContent = 'Please enter a folder name.';
|
|
719
|
+
createLauncherModal.input.focus();
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
const prompt = pendingCreateDetail.prompt || '';
|
|
724
|
+
const redirectUrl = `/pro?name=${encodeURIComponent(folderName)}&message=${encodeURIComponent(prompt)}`;
|
|
725
|
+
hideCreateLauncherModal();
|
|
726
|
+
window.location.href = redirectUrl;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function handleCreateModalKeydown(event) {
|
|
730
|
+
if (event.key === 'Enter') {
|
|
731
|
+
event.preventDefault();
|
|
732
|
+
confirmCreateLauncherModal();
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function handleCreateModalEscape(event) {
|
|
737
|
+
if (event.key === 'Escape' && pendingCreateDetail) {
|
|
738
|
+
event.preventDefault();
|
|
739
|
+
hideCreateLauncherModal();
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
function getCreateLauncherModal() {
|
|
744
|
+
if (createLauncherModal) {
|
|
745
|
+
return createLauncherModal;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const overlay = document.createElement('div');
|
|
749
|
+
overlay.className = 'create-launcher-modal-overlay';
|
|
750
|
+
overlay.style.display = 'none';
|
|
751
|
+
|
|
752
|
+
const modalContent = document.createElement('div');
|
|
753
|
+
modalContent.className = 'create-launcher-modal';
|
|
754
|
+
|
|
755
|
+
const title = document.createElement('h3');
|
|
756
|
+
title.textContent = 'Create';
|
|
757
|
+
|
|
758
|
+
const description = document.createElement('p');
|
|
759
|
+
description.className = 'create-launcher-modal-description';
|
|
760
|
+
description.textContent = 'Enter a prompt in the search bar to describe your launcher.';
|
|
761
|
+
|
|
762
|
+
const label = document.createElement('label');
|
|
763
|
+
label.className = 'create-launcher-modal-label';
|
|
764
|
+
label.textContent = 'Folder name';
|
|
765
|
+
|
|
766
|
+
const input = document.createElement('input');
|
|
767
|
+
input.type = 'text';
|
|
768
|
+
input.className = 'create-launcher-modal-input';
|
|
769
|
+
input.placeholder = 'example: my-launcher';
|
|
770
|
+
|
|
771
|
+
const error = document.createElement('div');
|
|
772
|
+
error.className = 'create-launcher-modal-error';
|
|
773
|
+
|
|
774
|
+
const actions = document.createElement('div');
|
|
775
|
+
actions.className = 'create-launcher-modal-actions';
|
|
776
|
+
|
|
777
|
+
const cancelButton = document.createElement('button');
|
|
778
|
+
cancelButton.type = 'button';
|
|
779
|
+
cancelButton.className = 'create-launcher-modal-button cancel';
|
|
780
|
+
cancelButton.textContent = 'Cancel';
|
|
781
|
+
|
|
782
|
+
const confirmButton = document.createElement('button');
|
|
783
|
+
confirmButton.type = 'button';
|
|
784
|
+
confirmButton.className = 'create-launcher-modal-button confirm';
|
|
785
|
+
confirmButton.textContent = 'OK';
|
|
786
|
+
|
|
787
|
+
actions.appendChild(cancelButton);
|
|
788
|
+
actions.appendChild(confirmButton);
|
|
789
|
+
|
|
790
|
+
label.appendChild(input);
|
|
791
|
+
modalContent.appendChild(title);
|
|
792
|
+
modalContent.appendChild(description);
|
|
793
|
+
modalContent.appendChild(label);
|
|
794
|
+
modalContent.appendChild(error);
|
|
795
|
+
modalContent.appendChild(actions);
|
|
796
|
+
overlay.appendChild(modalContent);
|
|
797
|
+
document.body.appendChild(overlay);
|
|
798
|
+
|
|
799
|
+
overlay.addEventListener('click', function(event) {
|
|
800
|
+
if (event.target === overlay) {
|
|
801
|
+
hideCreateLauncherModal();
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
cancelButton.addEventListener('click', hideCreateLauncherModal);
|
|
806
|
+
confirmButton.addEventListener('click', confirmCreateLauncherModal);
|
|
807
|
+
input.addEventListener('keydown', handleCreateModalKeydown);
|
|
808
|
+
|
|
809
|
+
createLauncherModal = {
|
|
810
|
+
overlay,
|
|
811
|
+
modal: modalContent,
|
|
812
|
+
input,
|
|
813
|
+
cancelButton,
|
|
814
|
+
confirmButton,
|
|
815
|
+
error,
|
|
816
|
+
description
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
return createLauncherModal;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function generateFolderName(prompt) {
|
|
823
|
+
if (!prompt) return '';
|
|
824
|
+
const normalized = prompt
|
|
825
|
+
.toLowerCase()
|
|
826
|
+
.replace(/[^a-z0-9\-\s_]/g, '')
|
|
827
|
+
.replace(/[\s_]+/g, '-');
|
|
828
|
+
return normalized.replace(/^-+|-+$/g, '').slice(0, 50);
|
|
829
|
+
}
|
|
636
830
|
}
|
|
637
831
|
|
|
638
832
|
// Auto-initialize if DOM is already loaded, otherwise wait for it
|
package/server/views/404.ejs
CHANGED
package/server/views/500.ejs
CHANGED
package/server/views/app.ejs
CHANGED
|
@@ -422,7 +422,7 @@ body .frame-link.selected {
|
|
|
422
422
|
/*
|
|
423
423
|
background: rgba(0,0,0,0.06) !important;
|
|
424
424
|
*/
|
|
425
|
-
background:
|
|
425
|
+
background: rgba(127, 91, 243, 0.9) !important;
|
|
426
426
|
color: white !important;
|
|
427
427
|
}
|
|
428
428
|
.frame-link.selected .del {
|
|
@@ -442,8 +442,8 @@ body.dark .frame-link.selected {
|
|
|
442
442
|
flex-shrink: 0;
|
|
443
443
|
}
|
|
444
444
|
.loader .btn:hover {
|
|
445
|
-
color:
|
|
446
|
-
border-color:
|
|
445
|
+
color: rgba(127, 91, 243, 0.9);
|
|
446
|
+
border-color: rgba(127, 91, 243, 0.9);
|
|
447
447
|
}
|
|
448
448
|
.loader .btn {
|
|
449
449
|
padding: 4px 8px;
|
|
@@ -648,7 +648,7 @@ nav .logo {
|
|
|
648
648
|
.error-message {
|
|
649
649
|
width: 100%;
|
|
650
650
|
/*
|
|
651
|
-
background:
|
|
651
|
+
background: rgba(127, 91, 243, 0.9) !important;
|
|
652
652
|
*/
|
|
653
653
|
color: white;
|
|
654
654
|
display: flex;
|
|
@@ -792,15 +792,11 @@ body.dark .submenu {
|
|
|
792
792
|
overflow: auto;
|
|
793
793
|
display: flex;
|
|
794
794
|
flex-grow: 1;
|
|
795
|
-
/*
|
|
796
795
|
border-top: 1px solid rgba(0, 0,0 ,0.04);
|
|
797
|
-
*/
|
|
798
796
|
}
|
|
799
|
-
/*
|
|
800
797
|
body.dark .appcanvas {
|
|
801
798
|
border-top: 1px solid rgba(255,255,255,0.04);
|
|
802
799
|
}
|
|
803
|
-
*/
|
|
804
800
|
.filler {
|
|
805
801
|
display: none;
|
|
806
802
|
}
|
|
@@ -841,10 +837,12 @@ body.dark .top-menu .btn2.selected {
|
|
|
841
837
|
}
|
|
842
838
|
|
|
843
839
|
body.dark #fs-status {
|
|
844
|
-
|
|
840
|
+
border-bottom: 1px solid rgba(255,255,255,0.04);
|
|
845
841
|
}
|
|
846
842
|
#fs-status {
|
|
843
|
+
/*
|
|
847
844
|
background: rgba(0,0,0,0.04);
|
|
845
|
+
*/
|
|
848
846
|
gap: 5px;
|
|
849
847
|
box-sizing: border-box;
|
|
850
848
|
/*
|
|
@@ -856,7 +854,8 @@ body.dark #fs-status {
|
|
|
856
854
|
/*
|
|
857
855
|
border-radius: 6px;
|
|
858
856
|
*/
|
|
859
|
-
justify-content: flex-
|
|
857
|
+
justify-content: flex-start;
|
|
858
|
+
border-bottom: 1px solid rgba(0,0,0,0.04);
|
|
860
859
|
}
|
|
861
860
|
|
|
862
861
|
.fs-status-btn {
|
|
@@ -1185,7 +1184,7 @@ body.dark .pinokio-commit-message-input:focus {
|
|
|
1185
1184
|
}
|
|
1186
1185
|
|
|
1187
1186
|
.pinokio-save-version-btn {
|
|
1188
|
-
background:
|
|
1187
|
+
background: rgba(127, 91, 243, 0.9) !important;
|
|
1189
1188
|
color: white !important;
|
|
1190
1189
|
border: 1px solid rgba(31, 35, 40, 0.15) !important;
|
|
1191
1190
|
border-radius: 6px !important;
|
|
@@ -1865,7 +1864,10 @@ body.minimized #fs-status {
|
|
|
1865
1864
|
</div>
|
|
1866
1865
|
<div class='menu-container'>
|
|
1867
1866
|
<div class='m n system' data-type="n">
|
|
1867
|
+
<a target="<%=src%>" href="<%=src%>" class='btn header-item frame-link' data-index="0" data-mode="refresh">
|
|
1868
|
+
<!--
|
|
1868
1869
|
<a id='file-browse' target="file-browse" href="<%=asset%>" class='btn header-item frame-link' data-index="0">
|
|
1870
|
+
-->
|
|
1869
1871
|
<div class='tab'>
|
|
1870
1872
|
<i class="fa-regular fa-folder-open"></i> Files
|
|
1871
1873
|
</div>
|
|
@@ -1903,20 +1905,6 @@ body.minimized #fs-status {
|
|
|
1903
1905
|
<%- include('./partials/dynamic', { dynamic: plugin_menu, }) %>
|
|
1904
1906
|
<% } %>
|
|
1905
1907
|
</div>
|
|
1906
|
-
<% if (false) { %>
|
|
1907
|
-
<% if (type !== "run") { %>
|
|
1908
|
-
<% if (plugin_menu) { %>
|
|
1909
|
-
<%- include('./partials/dynamic', { dynamic: plugin_menu, }) %>
|
|
1910
|
-
<% } else { %>
|
|
1911
|
-
<div class="nested-menu">
|
|
1912
|
-
<div class="btn header-item frame-link selected">
|
|
1913
|
-
<div class="tab"><i class="fa-solid fa-code"></i> Dev</div>
|
|
1914
|
-
<div class="loader"><i class="fa-solid fa-circle-notch fa-spin"></i></div>
|
|
1915
|
-
</div>
|
|
1916
|
-
</div>
|
|
1917
|
-
<% } %>
|
|
1918
|
-
<% } %>
|
|
1919
|
-
<% } %>
|
|
1920
1908
|
</div>
|
|
1921
1909
|
<% if (type === "browse") { %>
|
|
1922
1910
|
<div class='nested-menu selected git blue'>
|
|
@@ -3050,7 +3038,7 @@ body.minimized #fs-status {
|
|
|
3050
3038
|
const try_dynamic = async () => {
|
|
3051
3039
|
let rendered
|
|
3052
3040
|
let status
|
|
3053
|
-
const dynamic = await fetch("
|
|
3041
|
+
const dynamic = await fetch("<%-dynamic%>").then((res) => {
|
|
3054
3042
|
status = res.status
|
|
3055
3043
|
return res.text()
|
|
3056
3044
|
})
|
|
@@ -3065,10 +3053,16 @@ body.minimized #fs-status {
|
|
|
3065
3053
|
})
|
|
3066
3054
|
return
|
|
3067
3055
|
}
|
|
3056
|
+
console.log("DYNAMIC", dynamic)
|
|
3068
3057
|
if (dynamic && dynamic.length > 0) {
|
|
3069
3058
|
if (document.querySelector(".dynamic .submenu")) {
|
|
3070
3059
|
document.querySelector(".dynamic .submenu").innerHTML = dynamic
|
|
3071
3060
|
}
|
|
3061
|
+
let default_selection = document.querySelector(".dynamic .submenu [data-default]")
|
|
3062
|
+
if (default_selection) {
|
|
3063
|
+
console.log("CLICK default")
|
|
3064
|
+
default_selection.click()
|
|
3065
|
+
}
|
|
3072
3066
|
rendered = true
|
|
3073
3067
|
} else {
|
|
3074
3068
|
rendered = false
|
|
@@ -3235,8 +3229,9 @@ body.minimized #fs-status {
|
|
|
3235
3229
|
if (selection_url) {
|
|
3236
3230
|
selection = document.querySelector(`[href='${selection_url}']`)
|
|
3237
3231
|
} else {
|
|
3238
|
-
selection = document.querySelector("#devtab")
|
|
3232
|
+
// selection = document.querySelector("#devtab")
|
|
3239
3233
|
}
|
|
3234
|
+
console.log("Selection", selection)
|
|
3240
3235
|
if (selection) {
|
|
3241
3236
|
// setTimeout(() => {
|
|
3242
3237
|
selection.click()
|
|
@@ -4062,15 +4057,16 @@ body.minimized #fs-status {
|
|
|
4062
4057
|
let selected_tab = document.querySelector("aside .frame-link.selected")
|
|
4063
4058
|
let needs_selection
|
|
4064
4059
|
if (selected_tab) {
|
|
4065
|
-
if (
|
|
4066
|
-
|
|
4060
|
+
if (foreground_frame) {
|
|
4061
|
+
if (selected_tab.target === foreground_frame.name) {
|
|
4062
|
+
// do nothing
|
|
4063
|
+
} else {
|
|
4064
|
+
selected_tab.click()
|
|
4065
|
+
}
|
|
4067
4066
|
} else {
|
|
4068
|
-
|
|
4067
|
+
selected_tab.click()
|
|
4069
4068
|
}
|
|
4070
4069
|
} else {
|
|
4071
|
-
needs_selection = true
|
|
4072
|
-
}
|
|
4073
|
-
if (needs_selection) {
|
|
4074
4070
|
let selection_tab = document.querySelector(`aside .frame-link[target="${foreground_frame.name}"]`)
|
|
4075
4071
|
selection_tab.click()
|
|
4076
4072
|
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Pinokio Create Bookmarklet</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
color-scheme: light dark;
|
|
10
|
+
font-family: "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
11
|
+
line-height: 1.55;
|
|
12
|
+
}
|
|
13
|
+
body {
|
|
14
|
+
margin: 0;
|
|
15
|
+
padding: 24px 18px 48px;
|
|
16
|
+
background: rgba(248, 250, 255, 0.92);
|
|
17
|
+
color: #0f172a;
|
|
18
|
+
}
|
|
19
|
+
body.dark {
|
|
20
|
+
background: #0f172a;
|
|
21
|
+
color: rgba(226, 232, 240, 0.96);
|
|
22
|
+
}
|
|
23
|
+
.page {
|
|
24
|
+
max-width: 720px;
|
|
25
|
+
margin: 0 auto;
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
gap: 20px;
|
|
29
|
+
background: rgba(255, 255, 255, 0.88);
|
|
30
|
+
border-radius: 20px;
|
|
31
|
+
padding: 32px 36px;
|
|
32
|
+
box-shadow: 0 40px 100px rgba(15, 23, 42, 0.25);
|
|
33
|
+
}
|
|
34
|
+
body.dark .page {
|
|
35
|
+
background: rgba(17, 24, 39, 0.85);
|
|
36
|
+
box-shadow: 0 50px 120px rgba(2, 6, 20, 0.65);
|
|
37
|
+
}
|
|
38
|
+
h1 {
|
|
39
|
+
margin: 0;
|
|
40
|
+
font-size: 28px;
|
|
41
|
+
letter-spacing: -0.01em;
|
|
42
|
+
}
|
|
43
|
+
p {
|
|
44
|
+
margin: 0;
|
|
45
|
+
}
|
|
46
|
+
.bookmarklet-button {
|
|
47
|
+
display: inline-flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
justify-content: center;
|
|
50
|
+
padding: 12px 18px;
|
|
51
|
+
border-radius: 999px;
|
|
52
|
+
font-weight: 600;
|
|
53
|
+
font-size: 15px;
|
|
54
|
+
text-decoration: none;
|
|
55
|
+
color: #fff;
|
|
56
|
+
background: linear-gradient(135deg, rgba(127, 91, 243, 0.95), rgba(84, 63, 196, 0.95));
|
|
57
|
+
box-shadow: 0 18px 40px rgba(111, 76, 242, 0.32);
|
|
58
|
+
cursor: grab;
|
|
59
|
+
}
|
|
60
|
+
.bookmarklet-button:active {
|
|
61
|
+
cursor: grabbing;
|
|
62
|
+
}
|
|
63
|
+
.steps {
|
|
64
|
+
list-style: decimal;
|
|
65
|
+
margin: 0 0 0 20px;
|
|
66
|
+
padding: 0;
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
gap: 10px;
|
|
70
|
+
font-size: 15px;
|
|
71
|
+
}
|
|
72
|
+
.code-block {
|
|
73
|
+
display: flex;
|
|
74
|
+
flex-direction: column;
|
|
75
|
+
gap: 8px;
|
|
76
|
+
}
|
|
77
|
+
textarea {
|
|
78
|
+
width: 100%;
|
|
79
|
+
min-height: 120px;
|
|
80
|
+
border-radius: 14px;
|
|
81
|
+
border: 1px solid rgba(148, 163, 184, 0.3);
|
|
82
|
+
background: rgba(248, 250, 255, 0.7);
|
|
83
|
+
padding: 14px 16px;
|
|
84
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
85
|
+
font-size: 14px;
|
|
86
|
+
color: inherit;
|
|
87
|
+
resize: vertical;
|
|
88
|
+
}
|
|
89
|
+
body.dark textarea {
|
|
90
|
+
background: rgba(30, 41, 59, 0.6);
|
|
91
|
+
border-color: rgba(148, 163, 184, 0.35);
|
|
92
|
+
}
|
|
93
|
+
button.copy-btn {
|
|
94
|
+
align-self: flex-start;
|
|
95
|
+
padding: 10px 16px;
|
|
96
|
+
border-radius: 999px;
|
|
97
|
+
border: none;
|
|
98
|
+
font-weight: 600;
|
|
99
|
+
font-size: 14px;
|
|
100
|
+
cursor: pointer;
|
|
101
|
+
color: #fff;
|
|
102
|
+
background: rgba(15, 23, 42, 0.85);
|
|
103
|
+
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.25);
|
|
104
|
+
}
|
|
105
|
+
body.dark button.copy-btn {
|
|
106
|
+
background: rgba(148, 163, 184, 0.85);
|
|
107
|
+
color: #0f172a;
|
|
108
|
+
box-shadow: 0 12px 28px rgba(15, 23, 42, 0.45);
|
|
109
|
+
}
|
|
110
|
+
small {
|
|
111
|
+
opacity: 0.75;
|
|
112
|
+
font-size: 13px;
|
|
113
|
+
}
|
|
114
|
+
@media (max-width: 640px) {
|
|
115
|
+
body {
|
|
116
|
+
padding: 18px 12px 36px;
|
|
117
|
+
}
|
|
118
|
+
.page {
|
|
119
|
+
padding: 24px;
|
|
120
|
+
border-radius: 16px;
|
|
121
|
+
}
|
|
122
|
+
h1 {
|
|
123
|
+
font-size: 24px;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
</style>
|
|
127
|
+
</head>
|
|
128
|
+
<body class="<%= theme %>">
|
|
129
|
+
<main class="page">
|
|
130
|
+
<header>
|
|
131
|
+
<h1>Pinokio “Create” Bookmarklet</h1>
|
|
132
|
+
<p>Drop this bookmarklet onto your bookmarks bar. Clicking it on any site opens Pinokio’s Create modal with that page’s URL pre-filled in the prompt.</p>
|
|
133
|
+
</header>
|
|
134
|
+
|
|
135
|
+
<section>
|
|
136
|
+
<a class="bookmarklet-button" href="<%- bookmarkletHref %>">Pinokio Create</a>
|
|
137
|
+
<small>Tip: Drag the button to your bookmarks bar, or right-click → “Add to Favorites/Bookmarks”.</small>
|
|
138
|
+
</section>
|
|
139
|
+
|
|
140
|
+
<section>
|
|
141
|
+
<ol class="steps">
|
|
142
|
+
<li>Make sure your bookmarks bar is visible (View → Toolbars → Bookmarks Toolbar, or press <kbd>Ctrl</kbd>/<kbd>Cmd</kbd> + <kbd>Shift</kbd> + <kbd>B</kbd>).</li>
|
|
143
|
+
<li>Drag the “Pinokio Create” button above to the bar.</li>
|
|
144
|
+
<li>When you are on a page you want to automate, click the bookmark. Pinokio will open at <code><%= baseUrl %>/?create=1</code> with the current page URL in the prompt field.</li>
|
|
145
|
+
</ol>
|
|
146
|
+
</section>
|
|
147
|
+
|
|
148
|
+
<section class="code-block">
|
|
149
|
+
<strong>Manual option</strong>
|
|
150
|
+
<p>If dragging doesn’t work, create a new bookmark manually and paste the code below as the URL.</p>
|
|
151
|
+
<textarea id="bookmarklet-code" readonly><%- bookmarkletHref %></textarea>
|
|
152
|
+
<button type="button" class="copy-btn" data-copy>Copy bookmarklet code</button>
|
|
153
|
+
<small class="copy-status" hidden>Copied!</small>
|
|
154
|
+
</section>
|
|
155
|
+
</main>
|
|
156
|
+
|
|
157
|
+
<script>
|
|
158
|
+
(function() {
|
|
159
|
+
const copyBtn = document.querySelector('[data-copy]');
|
|
160
|
+
const textarea = document.getElementById('bookmarklet-code');
|
|
161
|
+
const status = document.querySelector('.copy-status');
|
|
162
|
+
|
|
163
|
+
if (!copyBtn || !textarea) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
copyBtn.addEventListener('click', async () => {
|
|
168
|
+
const text = textarea.value;
|
|
169
|
+
try {
|
|
170
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
171
|
+
await navigator.clipboard.writeText(text);
|
|
172
|
+
} else {
|
|
173
|
+
textarea.select();
|
|
174
|
+
document.execCommand('copy');
|
|
175
|
+
}
|
|
176
|
+
if (status) {
|
|
177
|
+
status.hidden = false;
|
|
178
|
+
status.textContent = 'Copied!';
|
|
179
|
+
setTimeout(() => {
|
|
180
|
+
status.hidden = true;
|
|
181
|
+
}, 1800);
|
|
182
|
+
}
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (status) {
|
|
185
|
+
status.hidden = false;
|
|
186
|
+
status.textContent = 'Copy failed. Please copy manually.';
|
|
187
|
+
setTimeout(() => {
|
|
188
|
+
status.hidden = true;
|
|
189
|
+
status.textContent = 'Copied!';
|
|
190
|
+
}, 2600);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
})();
|
|
195
|
+
</script>
|
|
196
|
+
</body>
|
|
197
|
+
</html>
|
|
@@ -81,7 +81,7 @@ body {
|
|
|
81
81
|
}
|
|
82
82
|
.item .title {
|
|
83
83
|
text-decoration: none;
|
|
84
|
-
color:
|
|
84
|
+
color: rgba(127, 91, 243, 0.9);
|
|
85
85
|
}
|
|
86
86
|
.item .col {
|
|
87
87
|
padding: 10px;
|
|
@@ -111,7 +111,7 @@ main {
|
|
|
111
111
|
width: 100%;
|
|
112
112
|
}
|
|
113
113
|
a {
|
|
114
|
-
color:
|
|
114
|
+
color: rgba(127, 91, 243, 0.9);
|
|
115
115
|
}
|
|
116
116
|
body.dark hr {
|
|
117
117
|
background: rgba(255,255,255,0.05);
|
|
@@ -135,7 +135,7 @@ td {
|
|
|
135
135
|
}
|
|
136
136
|
td.key {
|
|
137
137
|
width: 150px;
|
|
138
|
-
color:
|
|
138
|
+
color: rgba(127, 91, 243, 0.9);
|
|
139
139
|
}
|
|
140
140
|
.head {
|
|
141
141
|
padding: 30px;
|
|
@@ -167,7 +167,7 @@ pre {
|
|
|
167
167
|
margin: 20px;
|
|
168
168
|
}
|
|
169
169
|
.card:hover {
|
|
170
|
-
color:
|
|
170
|
+
color: rgba(127, 91, 243, 0.9) !important;
|
|
171
171
|
}
|
|
172
172
|
.card .desc {
|
|
173
173
|
opacity: 0.7;
|