Workspaces now fully functional (using indexedDB)
Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
parent
827349bf05
commit
c4ef6ccce4
15 changed files with 397 additions and 49 deletions
|
@ -1,5 +1,6 @@
|
||||||
:root {
|
:root {
|
||||||
--c-primary: #2c3333;
|
--c-primary: #2c3333;
|
||||||
|
--c-disabled: rgb(81, 81, 81);
|
||||||
--c-hover: hsl(180, 7%, 30%);
|
--c-hover: hsl(180, 7%, 30%);
|
||||||
--c-active: hsl(180, 7%, 25%);
|
--c-active: hsl(180, 7%, 25%);
|
||||||
--c-primary-accent: hsl(180, 7%, 40%);
|
--c-primary-accent: hsl(180, 7%, 40%);
|
||||||
|
|
|
@ -105,6 +105,39 @@
|
||||||
mask-image: url("../res/icons/paintbrush.svg");
|
mask-image: url("../res/icons/paintbrush.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui.inline-icon.icon-save::after,
|
||||||
|
.ui.icon > .icon-save {
|
||||||
|
-webkit-mask-image: url("../res/icons/save.svg");
|
||||||
|
mask-image: url("../res/icons/save.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui.inline-icon.icon-pencil::after,
|
||||||
|
.ui.icon > .icon-pencil {
|
||||||
|
-webkit-mask-image: url("../res/icons/pencil.svg");
|
||||||
|
mask-image: url("../res/icons/pencil.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui.inline-icon.icon-download::after,
|
||||||
|
.ui.icon > .icon-download {
|
||||||
|
-webkit-mask-image: url("../res/icons/download.svg");
|
||||||
|
mask-image: url("../res/icons/download.svg");
|
||||||
|
}
|
||||||
|
.ui.inline-icon.icon-upload::after,
|
||||||
|
.ui.icon > .icon-upload {
|
||||||
|
-webkit-mask-image: url("../res/icons/upload.svg");
|
||||||
|
mask-image: url("../res/icons/upload.svg");
|
||||||
|
}
|
||||||
|
.ui.inline-icon.icon-more-horizontal::after,
|
||||||
|
.ui.icon > .icon-more-horizontal {
|
||||||
|
-webkit-mask-image: url("../res/icons/more-horizontal.svg");
|
||||||
|
mask-image: url("../res/icons/more-horizontal.svg");
|
||||||
|
}
|
||||||
|
.ui.inline-icon.icon-trash::after,
|
||||||
|
.ui.icon > .icon-trash {
|
||||||
|
-webkit-mask-image: url("../res/icons/trash.svg");
|
||||||
|
mask-image: url("../res/icons/trash.svg");
|
||||||
|
}
|
||||||
|
|
||||||
.ui.inline-icon.icon-expand::after,
|
.ui.inline-icon.icon-expand::after,
|
||||||
.ui.icon > .icon-expand {
|
.ui.icon > .icon-expand {
|
||||||
-webkit-mask-image: url("../res/icons/expand.svg");
|
-webkit-mask-image: url("../res/icons/expand.svg");
|
||||||
|
|
59
css/ui/workspace.css
Normal file
59
css/ui/workspace.css
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#workspace-select input.autocomplete-text {
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
|
||||||
|
padding-left: 5px;
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspace-select-area .buttons > *:last-child {
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-btn {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
border: 0;
|
||||||
|
width: 21px;
|
||||||
|
height: 21px;
|
||||||
|
|
||||||
|
background-color: var(--c-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-btn:disabled {
|
||||||
|
cursor: default;
|
||||||
|
background-color: var(--c-disabled) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-btn:hover {
|
||||||
|
background-color: var(--c-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-btn:active {
|
||||||
|
background-color: var(--c-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-collapsible {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
width: 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-collapsible > *:first-child {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
width: fit-content;
|
||||||
|
height: 21px;
|
||||||
|
|
||||||
|
transition-duration: 50ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-collapsible.collapsed > *:first-child {
|
||||||
|
width: 0 !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
47
index.html
47
index.html
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
<link href="css/ui/generic.css?v=30837f8" rel="stylesheet" />
|
<link href="css/ui/generic.css?v=30837f8" rel="stylesheet" />
|
||||||
|
|
||||||
|
<link href="css/ui/workspace.css?v=30837f8" rel="stylesheet" />
|
||||||
<link href="css/ui/history.css?v=0b03861" rel="stylesheet" />
|
<link href="css/ui/history.css?v=0b03861" rel="stylesheet" />
|
||||||
<link href="css/ui/layers.css?v=1d66c2b" rel="stylesheet" />
|
<link href="css/ui/layers.css?v=1d66c2b" rel="stylesheet" />
|
||||||
<link href="css/ui/toolbar.css?v=109c78f" rel="stylesheet" />
|
<link href="css/ui/toolbar.css?v=109c78f" rel="stylesheet" />
|
||||||
|
@ -38,6 +39,46 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="info" class="menu-container" style="min-width: 200px">
|
<div id="info" class="menu-container" style="min-width: 200px">
|
||||||
|
<div
|
||||||
|
id="workspace-select-area"
|
||||||
|
style="display: flex; margin-bottom: 5px">
|
||||||
|
<div id="workspace-select"></div>
|
||||||
|
<div class="buttons" style="display: flex">
|
||||||
|
<button
|
||||||
|
id="save-workspace-btn"
|
||||||
|
class="ui inline-icon icon-save workspace-btn"
|
||||||
|
title="Save Workspace"></button>
|
||||||
|
<button
|
||||||
|
id="rename-workspace-btn"
|
||||||
|
class="ui inline-icon icon-pencil workspace-btn"
|
||||||
|
title="Rename Workspace"></button>
|
||||||
|
<button
|
||||||
|
id="more-workspace-btn"
|
||||||
|
class="ui inline-icon icon-more-horizontal workspace-btn"
|
||||||
|
title="More Options"></button>
|
||||||
|
<div
|
||||||
|
id="more-workspace-menu"
|
||||||
|
class="workspace-collapsible collapsed">
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
id="export-workspace-btn"
|
||||||
|
class="ui inline-icon icon-download workspace-btn"
|
||||||
|
title="Export Workspace"></button>
|
||||||
|
<button
|
||||||
|
id="import-workspace-btn"
|
||||||
|
class="ui inline-icon icon-upload workspace-btn"
|
||||||
|
title="Import Workspace"></button>
|
||||||
|
<button
|
||||||
|
id="delete-workspace-btn"
|
||||||
|
class="ui inline-icon icon-trash workspace-btn"
|
||||||
|
title="Delete Workspace"></button>
|
||||||
|
</div>
|
||||||
|
<div style="width: 5px; background-color: var(--c-primary)"></div>
|
||||||
|
</div>
|
||||||
|
<div style="width: 5px; background-color: var(--c-primary)"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="host-field-wrapper">
|
<div class="host-field-wrapper">
|
||||||
<div class="host-field">
|
<div class="host-field">
|
||||||
<div class="label">Host</div>
|
<div class="label">Host</div>
|
||||||
|
@ -164,8 +205,6 @@
|
||||||
<!-- Save/load image section -->
|
<!-- Save/load image section -->
|
||||||
<button type="button" class="collapsible">Save/Upscaling</button>
|
<button type="button" class="collapsible">Save/Upscaling</button>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<button onclick="saveWorkspaceToFile()">Save Workspace</button>
|
|
||||||
<button onclick="loadWorkspaceFromFile()">Load Workspace</button>
|
|
||||||
<button onclick="downloadCanvas()">Save canvas</button>
|
<button onclick="downloadCanvas()">Save canvas</button>
|
||||||
<br />
|
<br />
|
||||||
<label>Choose upscaler</label>
|
<label>Choose upscaler</label>
|
||||||
|
@ -365,6 +404,7 @@
|
||||||
<script
|
<script
|
||||||
src="js/lib/workspaces.js?v=4fbd55b"
|
src="js/lib/workspaces.js?v=4fbd55b"
|
||||||
type="text/javascript"></script>
|
type="text/javascript"></script>
|
||||||
|
<script src="js/lib/db.js?v=ac30d16" type="text/javascript"></script>
|
||||||
<script src="js/lib/input.js?v=aa14afc" type="text/javascript"></script>
|
<script src="js/lib/input.js?v=aa14afc" type="text/javascript"></script>
|
||||||
<script src="js/lib/layers.js?v=1a452a1" type="text/javascript"></script>
|
<script src="js/lib/layers.js?v=1a452a1" type="text/javascript"></script>
|
||||||
<script src="js/lib/commands.js?v=262f0bf" type="text/javascript"></script>
|
<script src="js/lib/commands.js?v=262f0bf" type="text/javascript"></script>
|
||||||
|
@ -412,6 +452,9 @@
|
||||||
type="text/javascript"></script>
|
type="text/javascript"></script>
|
||||||
|
|
||||||
<!-- Initialize -->
|
<!-- Initialize -->
|
||||||
|
<script
|
||||||
|
src="js/initalize/workspace.populate.js?v=fd01c47"
|
||||||
|
type="text/javascript"></script>
|
||||||
<script
|
<script
|
||||||
src="js/initalize/shortcuts.populate.js?v=fd01c47"
|
src="js/initalize/shortcuts.populate.js?v=fd01c47"
|
||||||
type="text/javascript"></script>
|
type="text/javascript"></script>
|
||||||
|
|
15
js/index.js
15
js/index.js
|
@ -886,7 +886,7 @@ async function exportWorkspaceState() {
|
||||||
|
|
||||||
async function importWorkspaceState(state) {
|
async function importWorkspaceState(state) {
|
||||||
// Start from zero, effectively
|
// Start from zero, effectively
|
||||||
await commands.undo(commands._history.length);
|
await commands.clear();
|
||||||
|
|
||||||
// Setup initial layer
|
// Setup initial layer
|
||||||
const layer = uil.layerIndex.default;
|
const layer = uil.layerIndex.default;
|
||||||
|
@ -965,19 +965,6 @@ async function saveWorkspaceToFile() {
|
||||||
link.click();
|
link.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadWorkspaceFromFile() {
|
|
||||||
const input = document.createElement("input");
|
|
||||||
input.type = "file";
|
|
||||||
input.accept = "application/json";
|
|
||||||
input.addEventListener("change", async (evn) => {
|
|
||||||
let files = Array.from(input.files);
|
|
||||||
const json = await files[0].text();
|
|
||||||
|
|
||||||
importWorkspaceState(JSON.parse(json));
|
|
||||||
});
|
|
||||||
input.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getUpscalers() {
|
async function getUpscalers() {
|
||||||
/*
|
/*
|
||||||
so for some reason when upscalers request returns upscalers, the real-esrgan model names are incorrect, and need to be fetched from /sdapi/v1/realesrgan-models
|
so for some reason when upscalers request returns upscalers, the real-esrgan model names are incorrect, and need to be fetched from /sdapi/v1/realesrgan-models
|
||||||
|
|
179
js/initalize/workspace.populate.js
Normal file
179
js/initalize/workspace.populate.js
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
(() => {
|
||||||
|
const saveWorkspaceBtn = document.getElementById("save-workspace-btn");
|
||||||
|
const renameWorkspaceBtn = document.getElementById("rename-workspace-btn");
|
||||||
|
const moreWorkspaceBtn = document.getElementById("more-workspace-btn");
|
||||||
|
const expandedWorkspaceMenu = document.getElementById("more-workspace-menu");
|
||||||
|
const exportWorkspaceBtn = document.getElementById("export-workspace-btn");
|
||||||
|
const importWorkspaceBtn = document.getElementById("import-workspace-btn");
|
||||||
|
const deleteWorkspaceBtn = document.getElementById("delete-workspace-btn");
|
||||||
|
|
||||||
|
moreWorkspaceBtn.addEventListener("click", () => {
|
||||||
|
expandedWorkspaceMenu.classList.toggle("collapsed");
|
||||||
|
});
|
||||||
|
|
||||||
|
const workspaceAutocomplete = createAutoComplete(
|
||||||
|
"Workspace",
|
||||||
|
document.getElementById("workspace-select")
|
||||||
|
);
|
||||||
|
|
||||||
|
workspaceAutocomplete.options = [{name: "Default", value: "default"}];
|
||||||
|
workspaceAutocomplete.value = "default";
|
||||||
|
renameWorkspaceBtn.disabled = true;
|
||||||
|
deleteWorkspaceBtn.disabled = true;
|
||||||
|
|
||||||
|
workspaceAutocomplete.onchange.on(async ({name, value}) => {
|
||||||
|
if (value === "default") {
|
||||||
|
renameWorkspaceBtn.disabled = true;
|
||||||
|
deleteWorkspaceBtn.disabled = true;
|
||||||
|
await commands.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
renameWorkspaceBtn.disabled = false;
|
||||||
|
deleteWorkspaceBtn.disabled = false;
|
||||||
|
|
||||||
|
const workspaces = db
|
||||||
|
.transaction("workspaces", "readonly")
|
||||||
|
.objectStore("workspaces");
|
||||||
|
|
||||||
|
workspaces.get(value).onsuccess = (e) => {
|
||||||
|
console.debug("[workspace.populate] Loading workspace");
|
||||||
|
|
||||||
|
const res = e.target.result;
|
||||||
|
const {workspace} = res;
|
||||||
|
importWorkspaceState(workspace);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates Workspace selection list
|
||||||
|
*/
|
||||||
|
const listWorkspaces = async (value = undefined) => {
|
||||||
|
const options = [{name: "Default", value: "default"}];
|
||||||
|
|
||||||
|
const workspaces = db
|
||||||
|
.transaction("workspaces", "readonly")
|
||||||
|
.objectStore("workspaces");
|
||||||
|
|
||||||
|
workspaces.openCursor().onsuccess = (e) => {
|
||||||
|
/** @type {IDBCursor} */
|
||||||
|
const c = e.target.result;
|
||||||
|
if (c) {
|
||||||
|
options.push({name: c.value.name, value: c.key});
|
||||||
|
c.continue();
|
||||||
|
} else {
|
||||||
|
const previousValue = workspaceAutocomplete.value;
|
||||||
|
|
||||||
|
workspaceAutocomplete.options = options;
|
||||||
|
workspaceAutocomplete.value = value ?? previousValue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveWorkspaceToDB = async (value) => {
|
||||||
|
const workspace = await exportWorkspaceState();
|
||||||
|
|
||||||
|
const workspaces = db
|
||||||
|
.transaction("workspaces", "readwrite")
|
||||||
|
.objectStore("workspaces");
|
||||||
|
|
||||||
|
let id = value;
|
||||||
|
if (value === "default" && commands._history.length > 0) {
|
||||||
|
// If Workspace is the Default
|
||||||
|
const name = (prompt("Please enter the workspace name") ?? "").trim();
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
id = guid();
|
||||||
|
workspaces.add({id, name, workspace}).onsuccess = () => {
|
||||||
|
listWorkspaces(id);
|
||||||
|
alert(`Workspace saved as '${name}'`);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
workspaces.get(id).onsuccess = (e) => {
|
||||||
|
const ws = e.target.result;
|
||||||
|
if (ws) {
|
||||||
|
workspaces.put({id, workspace}).onsuccess = () => {
|
||||||
|
alert(`Workspace saved as '${ws.value.name}'`);
|
||||||
|
listWorkspaces();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normal Workspace Export/Import
|
||||||
|
exportWorkspaceBtn.addEventListener("click", () => saveWorkspaceToFile());
|
||||||
|
importWorkspaceBtn.addEventListener("click", () => {
|
||||||
|
const input = document.createElement("input");
|
||||||
|
input.type = "file";
|
||||||
|
input.accept = "application/json";
|
||||||
|
input.addEventListener("change", async (evn) => {
|
||||||
|
let files = Array.from(input.files);
|
||||||
|
const json = await files[0].text();
|
||||||
|
|
||||||
|
await importWorkspaceState(JSON.parse(json));
|
||||||
|
saveWorkspaceToDB("default");
|
||||||
|
});
|
||||||
|
input.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
const onDatabaseLoad = async () => {
|
||||||
|
// Get workspaces from database
|
||||||
|
listWorkspaces();
|
||||||
|
|
||||||
|
// Save Workspace Button
|
||||||
|
saveWorkspaceBtn.addEventListener(
|
||||||
|
"click",
|
||||||
|
saveWorkspaceToDB(workspaceAutocomplete.value)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Rename Workspace
|
||||||
|
renameWorkspaceBtn.addEventListener("click", () => {
|
||||||
|
const workspaces = db
|
||||||
|
.transaction("workspaces", "readwrite")
|
||||||
|
.objectStore("workspaces");
|
||||||
|
|
||||||
|
let id = workspaceAutocomplete.value;
|
||||||
|
|
||||||
|
workspaces.get(id).onsuccess = (e) => {
|
||||||
|
const workspace = e.target.result;
|
||||||
|
const name = prompt(
|
||||||
|
`Please enter the new workspace name.\nOriginal is '${workspace.name}'`
|
||||||
|
).trim();
|
||||||
|
|
||||||
|
if (!name) return;
|
||||||
|
|
||||||
|
workspace.name = name;
|
||||||
|
|
||||||
|
workspaces.put(workspace).onsuccess = () => {
|
||||||
|
listWorkspaces();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
// Delete Workspace
|
||||||
|
deleteWorkspaceBtn.addEventListener("click", () => {
|
||||||
|
const workspaces = db
|
||||||
|
.transaction("workspaces", "readwrite")
|
||||||
|
.objectStore("workspaces");
|
||||||
|
|
||||||
|
let id = workspaceAutocomplete.value;
|
||||||
|
|
||||||
|
workspaces.get(id).onsuccess = (e) => {
|
||||||
|
const workspace = e.target.result;
|
||||||
|
|
||||||
|
if (
|
||||||
|
confirm(
|
||||||
|
`Do you really want to delete the workspace '${workspace.name}'?`
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
workspaces.delete(id).onsuccess = (e) => {
|
||||||
|
listWorkspaces("default");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (db) onDatabaseLoad();
|
||||||
|
else ondatabaseload.on(onDatabaseLoad);
|
||||||
|
})();
|
|
@ -57,6 +57,21 @@ const commands = makeReadOnly(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the history
|
||||||
|
*/
|
||||||
|
async clear() {
|
||||||
|
await this.undo(this._history.length);
|
||||||
|
|
||||||
|
this._history.splice(0, this._history.length);
|
||||||
|
|
||||||
|
_commands_events.emit({
|
||||||
|
action: "clear",
|
||||||
|
state: {},
|
||||||
|
current: commands._current,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports an exported command and runs it
|
* Imports an exported command and runs it
|
||||||
*
|
*
|
||||||
|
|
36
js/lib/db.js
Normal file
36
js/lib/db.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
const idb = window.indexedDB.open("openoutpaint", 2);
|
||||||
|
|
||||||
|
idb.onerror = (e) => {
|
||||||
|
console.warn("[stamp] Failed to connect to IndexedDB");
|
||||||
|
console.warn(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
idb.onupgradeneeded = (e) => {
|
||||||
|
const db = e.target.result;
|
||||||
|
|
||||||
|
console.debug(`[stamp] Setting up database version ${db.version}`);
|
||||||
|
|
||||||
|
if (e.oldVersion < 1) {
|
||||||
|
// Resources Store
|
||||||
|
const resourcesStore = db.createObjectStore("resources", {
|
||||||
|
keyPath: "id",
|
||||||
|
});
|
||||||
|
resourcesStore.createIndex("name", "name", {unique: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workspaces Store
|
||||||
|
const workspacesStore = db.createObjectStore("workspaces", {
|
||||||
|
keyPath: "id",
|
||||||
|
});
|
||||||
|
workspacesStore.createIndex("name", "name", {unique: false});
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @type {IDBDatabase} */
|
||||||
|
let db = null;
|
||||||
|
/** @type {Observer<{db: IDBDatabase}>} */
|
||||||
|
const ondatabaseload = new Observer();
|
||||||
|
|
||||||
|
idb.onsuccess = (e) => {
|
||||||
|
db = e.target.result;
|
||||||
|
ondatabaseload.emit({db});
|
||||||
|
};
|
|
@ -209,7 +209,6 @@ function createSlider(name, wrapper, options = {}) {
|
||||||
* @param {{name: string, value: string, optionelcb: (el: HTMLOptionElement) => void}[]} options.options Options to add to the selector
|
* @param {{name: string, value: string, optionelcb: (el: HTMLOptionElement) => void}[]} options.options Options to add to the selector
|
||||||
* @param {object} extraEl Additional element to include in wrapper div (e.g. model refresh button)
|
* @param {object} extraEl Additional element to include in wrapper div (e.g. model refresh button)
|
||||||
* @param {string} extraClass Additional class to attach to the autocomplete input element
|
* @param {string} extraClass Additional class to attach to the autocomplete input element
|
||||||
* @returns {AutoCompleteElement}
|
|
||||||
*/
|
*/
|
||||||
function createAutoComplete(
|
function createAutoComplete(
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
_commands_events.on((message) => {
|
_commands_events.on((message) => {
|
||||||
if (message.action === "run") {
|
if (message.action === "run" || message.action === "clear") {
|
||||||
Array.from(historyView.children).forEach((child) => {
|
Array.from(historyView.children).forEach((child) => {
|
||||||
if (
|
if (
|
||||||
!commands._history.find((entry) => `hist-${entry.id}` === child.id)
|
!commands._history.find((entry) => `hist-${entry.id}` === child.id)
|
||||||
|
|
|
@ -146,13 +146,9 @@ const stampTool = () =>
|
||||||
};
|
};
|
||||||
|
|
||||||
// Open IndexedDB connection
|
// Open IndexedDB connection
|
||||||
const IDBOpenRequest = window.indexedDB.open("stamp", 1);
|
|
||||||
|
|
||||||
// Synchronizes resources array with the DOM and Local Storage
|
// Synchronizes resources array with the DOM and Local Storage
|
||||||
const syncResources = () => {
|
const syncResources = () => {
|
||||||
// Saves to IndexedDB
|
// Saves to IndexedDB
|
||||||
/** @type {IDBDatabase} */
|
|
||||||
const db = state.stampDB;
|
|
||||||
const resources = db
|
const resources = db
|
||||||
.transaction("resources", "readwrite")
|
.transaction("resources", "readwrite")
|
||||||
.objectStore("resources");
|
.objectStore("resources");
|
||||||
|
@ -590,35 +586,9 @@ const stampTool = () =>
|
||||||
state.ctxmenu.resourceList = resourceList;
|
state.ctxmenu.resourceList = resourceList;
|
||||||
|
|
||||||
// Performs resource fetch from IndexedDB
|
// Performs resource fetch from IndexedDB
|
||||||
|
const loadResources = async () => {
|
||||||
IDBOpenRequest.onerror = (e) => {
|
|
||||||
console.warn("[stamp] Failed to connect to IndexedDB");
|
|
||||||
console.warn(e);
|
|
||||||
};
|
|
||||||
|
|
||||||
IDBOpenRequest.onupgradeneeded = (e) => {
|
|
||||||
const db = e.target.result;
|
|
||||||
|
|
||||||
console.debug(`[stamp] Setting up database version ${db.version}`);
|
|
||||||
|
|
||||||
const resourcesStore = db.createObjectStore("resources", {
|
|
||||||
keyPath: "id",
|
|
||||||
});
|
|
||||||
resourcesStore.createIndex("name", "name", {unique: false});
|
|
||||||
};
|
|
||||||
|
|
||||||
IDBOpenRequest.onsuccess = async (e) => {
|
|
||||||
console.debug("[stamp] Connected to IndexedDB");
|
console.debug("[stamp] Connected to IndexedDB");
|
||||||
|
|
||||||
state.stampDB = e.target.result;
|
|
||||||
|
|
||||||
state.stampDB.onerror = (evn) => {
|
|
||||||
console.warn(`[stamp] Database Error:`);
|
|
||||||
console.warn(evn.target.errorCode);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @type {IDBDatabase} */
|
|
||||||
const db = state.stampDB;
|
|
||||||
/** @type {IDBRequest<{id: string, name: string, src: string}[]>} */
|
/** @type {IDBRequest<{id: string, name: string, src: string}[]>} */
|
||||||
const FetchAllTransaction = db
|
const FetchAllTransaction = db
|
||||||
.transaction("resources")
|
.transaction("resources")
|
||||||
|
@ -648,6 +618,9 @@ const stampTool = () =>
|
||||||
syncResources();
|
syncResources();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (db) loadResources();
|
||||||
|
else ondatabaseload.on(loadResources);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
populateContextMenu: (menu, state) => {
|
populateContextMenu: (menu, state) => {
|
||||||
|
|
6
res/icons/more-horizontal.svg
Normal file
6
res/icons/more-horizontal.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="1"></circle>
|
||||||
|
<circle cx="19" cy="12" r="1"></circle>
|
||||||
|
<circle cx="5" cy="12" r="1"></circle>
|
||||||
|
|
||||||
|
</svg>
|
After Width: | Height: | Size: 314 B |
5
res/icons/pencil.svg
Normal file
5
res/icons/pencil.svg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<line x1="18" y1="2" x2="22" y2="6"></line>
|
||||||
|
<path d="M7.5 20.5 19 9l-4-4L3.5 16.5 2 22z"></path>
|
||||||
|
|
||||||
|
</svg>
|
After Width: | Height: | Size: 290 B |
6
res/icons/save.svg
Normal file
6
res/icons/save.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
|
||||||
|
<polyline points="17 21 17 13 7 13 7 21"></polyline>
|
||||||
|
<polyline points="7 3 7 8 15 8"></polyline>
|
||||||
|
|
||||||
|
</svg>
|
After Width: | Height: | Size: 374 B |
6
res/icons/upload.svg
Normal file
6
res/icons/upload.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||||||
|
<polyline points="17 8 12 3 7 8"></polyline>
|
||||||
|
<line x1="12" y1="3" x2="12" y2="15"></line>
|
||||||
|
|
||||||
|
</svg>
|
After Width: | Height: | Size: 345 B |
Loading…
Reference in a new issue