Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: save and restore models with indexedDB
Browse files Browse the repository at this point in the history
j4k0xb committed May 17, 2024
1 parent 0e061d9 commit 1a65a4f
Showing 5 changed files with 3,448 additions and 2,707 deletions.
1 change: 1 addition & 0 deletions apps/playground/package.json
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@
"dependencies": {
"@babel/generator": "^7.24.4",
"@babel/types": "^7.24.0",
"idb": "^8.0.0",
"monaco-editor": "^0.47.0",
"sandybox": "^1.1.2",
"solid-js": "^1.8.16",
92 changes: 91 additions & 1 deletion apps/playground/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import * as monaco from 'monaco-editor';
import { For, Show, createMemo, createSignal, onCleanup } from 'solid-js';
import {
For,
Show,
batch,
createEffect,
createMemo,
createSignal,
onCleanup,
} from 'solid-js';
import { createStore } from 'solid-js/store';
import Breadcrumbs from './components/Breadcrumbs';
import MonacoEditor from './components/MonacoEditor';
import Sidebar from './components/Sidebar';
import Tab from './components/Tab';
import { DeobfuscateContextProvider } from './context/DeobfuscateContext';
import {
clearSavedModels,
loadSavedModels,
saveModels,
type SavedModel,
} from './indexeddb';
import type { DeobfuscateResult } from './webcrack.worker';

export const [settings, setSettings] = createStore({
@@ -29,6 +43,7 @@ function App() {
const [activeTab, setActiveTab] = createSignal<
monaco.editor.ITextModel | undefined
>(tabs()[0]);
const [savedModels, setSavedModels] = createSignal<SavedModel[]>([]);

const fileModels = createMemo(() =>
models().filter((m) => m.uri.scheme === 'file'),
@@ -40,6 +55,43 @@ function App() {
fileModels().map((model) => model.uri.path),
);

loadSavedModels().then(setSavedModels).catch(console.error);
setTimeout(() => setSavedModels([]), 5000);

createEffect(() => {
if (
models().length === 0 ||
models().every((m) => m.getValueLength() === 0)
) {
return;
}
saveModels(models()).catch(console.error);
});

function restoreSavedModels() {
batch(() => {
models().forEach((model) => model.dispose());

setModels(
savedModels().map((model) =>
monaco.editor.createModel(
model.value,
model.language,
monaco.Uri.parse(model.uri),
),
),
);
console.log(
'Loaded saved models',
savedModels().map((m) => m.uri),
);
setSavedModels([]);

setTabs(untitledModels());
setActiveTab(untitledModels()[0]);
});
}

onCleanup(() => {
models().forEach((model) => model.dispose());
});
@@ -142,6 +194,41 @@ function App() {
onResult={onDeobfuscateResult}
onError={onDeobfuscateError}
>
<Show when={savedModels().length > 0}>
<div role="alert" class="alert absolute z-10 bottom-5 right-5 max-w-md">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="stroke-info shrink-0 w-6 h-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
<span>
Restore {savedModels().length} saved tabs from last session?
</span>
<div>
<button
class="btn btn-sm"
onClick={() => {
void clearSavedModels();
setSavedModels([]);
}}
>
Clear
</button>
<button class="btn btn-sm btn-primary" onClick={restoreSavedModels}>
Load
</button>
</div>
</div>
</Show>

<div class="h-screen flex">
<Sidebar paths={filePaths()} onFileClick={openFile} />

@@ -182,6 +269,9 @@ function App() {
models={models()}
currentModel={activeTab()}
onModelChange={openTab}
onValueChange={() => {
saveModels(models()).catch(console.error);
}}
/>
</main>
</div>
6 changes: 6 additions & 0 deletions apps/playground/src/components/MonacoEditor.tsx
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ interface Props {
models: monaco.editor.ITextModel[];
currentModel?: monaco.editor.ITextModel;
onModelChange?: (model: monaco.editor.ITextModel) => void;
onValueChange?: (value: string) => void;
}

monaco.editor.defineTheme('dark', {
@@ -57,6 +58,11 @@ export default function MonacoEditor(props: Props) {
editor.focus();
}

editor.onDidChangeModelContent(() => {
const model = editor.getModel();
if (model) props.onValueChange?.(model.getValue());
});

// Go to definition
const editorOpener = monaco.editor.registerEditorOpener({
openCodeEditor(_source, resource, selectionOrPosition) {
57 changes: 57 additions & 0 deletions apps/playground/src/indexeddb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { openDB, type DBSchema } from 'idb';
import type * as monaco from 'monaco-editor';

export interface SavedModel {
value: string;
language: string;
uri: string;
}

interface MyDB extends DBSchema {
models: {
key: string;
value: SavedModel;
};
}

async function initDB() {
return openDB<MyDB>('models', 1, {
upgrade(db) {
db.createObjectStore('models', { keyPath: 'uri' });
},
});
}

export async function saveModels(models: monaco.editor.ITextModel[]) {
console.log('Saving models...', models.length);
const db = await initDB();
await db.clear('models');
await Promise.all(
models.map(async (model) => {
await db.add('models', {
value: model.getValue(),
language: model.getLanguageId(),
uri: model.uri.toString(),
});
}),
);
}

export async function clearSavedModels() {
const db = await initDB();
await db.clear('models');
}

export async function saveModel(model: monaco.editor.ITextModel) {
const db = await initDB();
await db.put('models', {
value: model.getValue(),
language: model.getLanguageId(),
uri: model.uri.toString(),
});
}

export async function loadSavedModels(): Promise<SavedModel[]> {
const db = await initDB();
return db.getAll('models');
}
5,999 changes: 3,293 additions & 2,706 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

0 comments on commit 1a65a4f

Please sign in to comment.