1
0
mirror of https://github.com/garraflavatra/rolens.git synced 2025-07-19 06:14:04 +00:00

Initial commit

This commit is contained in:
2023-01-10 17:28:27 +01:00
commit 101d892139
45 changed files with 7976 additions and 0 deletions

12
frontend/index.html Normal file
View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>mongodup</title>
</head>
<body>
<div id="app"></div>
<script src="./src/main.js" type="module"></script>
</body>
</html>

38
frontend/jsconfig.json Normal file
View File

@ -0,0 +1,38 @@
{
"compilerOptions": {
"moduleResolution": "Node",
"target": "ESNext",
"module": "ESNext",
/**
* svelte-preprocess cannot figure out whether you have
* a value or a type, so tell TypeScript to enforce using
* `import type` instead of `import` for Types.
*/
"importsNotUsedAsValues": "error",
"isolatedModules": true,
"resolveJsonModule": true,
/**
* To have warnings / errors of the Svelte compiler at the
* correct position, enable source maps by default.
*/
"sourceMap": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable this if you'd like to use dynamic types.
*/
"checkJs": true
},
/**
* Use global.d.ts instead of compilerOptions.types
* to avoid limiting type declarations.
*/
"include": [
"src/**/*.d.ts",
"src/**/*.js",
"src/**/*.svelte"
]
}

5430
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

22
frontend/package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^1.0.1",
"eslint": "^8.31.0",
"eslint-config-nodejs": "github:johbog/eslint-config-nodejs",
"eslint-config-svelte3": "github:johbog/eslint-config-svelte3",
"svelte": "^3.49.0",
"vite": "^3.0.0"
},
"eslintConfig": {
"extends": ["nodejs", "svelte3"]
}
}

1
frontend/package.json.md5 Executable file
View File

@ -0,0 +1 @@
2c80c12f4d36310f15d586e535d30d27

27
frontend/src/actions.js Normal file
View File

@ -0,0 +1,27 @@
export function input(node, { json } = { json: false }) {
const handleInput = () => {
if (json) {
try {
JSON.parse(node.value);
node.classList.remove('invalid');
}
catch {
node.classList.add('invalid');
}
}
};
const handleFocus = () => {
node.select();
};
node.addEventListener('focus', handleFocus);
node.addEventListener('input', handleInput);
return {
destroy: () => {
node.removeEventListener('focus', handleFocus);
node.removeEventListener('input', handleInput);
},
};
}

120
frontend/src/app.svelte Normal file
View File

@ -0,0 +1,120 @@
<script>
import { onMount } from 'svelte';
import { Hosts, OpenCollection, OpenConnection, OpenDatabase } from '../wailsjs/go/main/App';
import AddressBar from './organisms/addressbar/index.svelte';
import Grid from './components/grid.svelte';
import CollectionDetail from './organisms/collection-detail/index.svelte';
import { busy } from './stores';
const connections = {};
let hosts = {};
let activeHostKey = '';
let activeDbKey = '';
let activeCollKey = '';
let addressBarModalOpen = false;
$: host = hosts[activeHostKey];
$: connection = connections[activeHostKey];
$: database = connection?.databases[activeDbKey];
$: collection = database?.collections?.[activeCollKey];
async function openConnection(hostKey) {
$busy = true;
const databases = await OpenConnection(hostKey);
if (databases) {
connections[hostKey] = { databases: {} };
databases.forEach(dbKey => {
connections[hostKey].databases[dbKey] = { collections: {} };
});
activeHostKey = hostKey;
addressBarModalOpen = false;
window.runtime.WindowSetTitle(`${host.name} - Mongodup`);
}
$busy = false;
}
async function openDatabase(dbKey) {
$busy = true;
const collections = await OpenDatabase(activeHostKey, dbKey);
for (const collKey of collections || []) {
connections[activeHostKey].databases[dbKey].collections[collKey] = {};
}
$busy = false;
}
async function openCollection(collKey) {
$busy = true;
const stats = await OpenCollection(activeHostKey, activeDbKey, collKey);
connections[activeHostKey].databases[activeDbKey].collections[collKey].stats = stats;
$busy = false;
}
onMount(() => {
Hosts().then(h => hosts = h);
});
</script>
<main>
<AddressBar {hosts} bind:activeHostKey on:select={e => openConnection(e.detail)} bind:modalOpen={addressBarModalOpen} />
<div class="columns">
{#if host && connection}
<div class="hostlist">
<Grid
columns={[ { key: 'id' }, { key: 'collCount', right: true } ]}
items={Object.keys(connection.databases).map(id => ({
id,
collCount: Object.keys(connection.databases[id].collections || {}).length,
children: connection.databases[id].collections || [],
}))}
bind:activeKey={activeDbKey}
bind:activeChildKey={activeCollKey}
on:select={e => openDatabase(e.detail)}
on:selectChild={e => openCollection(e.detail)}
/>
</div>
<div class="collection">
<CollectionDetail
{collection}
hostKey={activeHostKey}
dbKey={activeDbKey}
collectionKey={activeCollKey}
/>
</div>
{/if}
</div>
</main>
<style>
main {
height: 100vh;
max-height: 100vh;
display: flex;
flex-direction: column;
}
.columns {
display: flex;
gap: 1rem;
flex: 1;
}
.columns > :global(*) {
height: 100%;
display: flex;
flex-direction: column;
}
.hostlist {
flex: 0 0 250px;
overflow-y: scroll;
}
.collection {
flex: 1;
width: auto;
}
</style>

View File

@ -0,0 +1,28 @@
<script>
export let code = '';
</script>
<div class="examplecode">
<strong>CLI command</strong>
<code>{code}</code>
</div>
<style>
.examplecode {
border: 1px solid #ccc;
border-radius: 10px;
padding: 0.5rem;
opacity: 0.6;
margin-bottom: 0.5rem;
}
strong {
display: inline-block;
margin-right: 1rem;
font-weight: 700;
}
code {
user-select: all;
}
</style>

View File

@ -0,0 +1,167 @@
<script>
import { createEventDispatcher } from 'svelte';
import Icon from './icon.svelte';
export let columns = [];
export let items = [];
export let key = 'id';
export let activeKey = '';
export let activeChildKey = '';
export let showHeaders = true;
export let level = 0;
export let contained = false;
const dispatch = createEventDispatcher();
const childrenOpen = {};
$: _items = objectToArray(items).map(item => {
item.children = objectToArray(item.children);
return item;
});
function objectToArray(obj) {
if (Array.isArray(obj)) {
return obj;
}
else if (typeof obj === 'object') {
return Object.entries(obj).map(([ k, item ]) => ({ ...item, [key]: k }));
}
else {
return obj;
}
}
function select(itemKey) {
activeKey = itemKey;
activeChildKey = '';
dispatch('select', itemKey);
}
function selectChild(itemKey, childKey) {
select(itemKey);
activeChildKey = childKey;
dispatch('selectChild', childKey);
}
function toggleChildren(itemKey) {
childrenOpen[itemKey] = !childrenOpen[itemKey];
}
</script>
<div class:grid={level === 0} class:subgrid={level > 0} class:contained>
<table>
{#if showHeaders && columns.some(col => col.title)}
<thead>
<tr>
<th class="has-toggle"></th>
{#each columns as column}
<th scope="col">{column.title || ''}</th>
{/each}
</tr>
</thead>
{/if}
<tbody>
{#each _items as item (item[key])}
<tr on:click={() => select(item[key])} class:selected={activeKey === item[key] && !activeChildKey}>
<td class="has-toggle">
{#if item.children}
<button class="toggle" on:click={() => toggleChildren(item[key])}>
<Icon name={childrenOpen[item[key]] ? 'chev-d' : 'chev-r'} />
</button>
{/if}
</td>
{#each columns as column}
{@const value = item[column.key]}
<td class:right={column.right}>
{#if typeof value !== 'object'}
{value || ''}
{/if}
</td>
{/each}
</tr>
{#if item.children && childrenOpen[item[key]]}
<tr>
<td></td>
<td colspan={columns.length + 1} class="subgrid-parent">
<svelte:self
{columns}
{key}
bind:activeKey={activeChildKey}
showHeaders={false}
items={item.children}
level={level + 1}
on:select={e => selectChild(item[key], e.detail)}
/>
</td>
</tr>
{/if}
{/each}
</tbody>
</table>
</div>
<style>
.grid {
background-color: #fff;
height: 100%;
width: 100%;
overflow: scroll;
}
.grid.contained {
border: 1px solid #ccc;
}
.subgrid {
width: 100%;
}
table {
border-collapse: collapse;
width: 100%;
}
table thead {
border-bottom: 2px solid #ccc;
}
table th {
font-weight: 600;
text-align: left;
}
tr {
cursor: pointer;
}
td, th {
padding: 0.3rem;
height: 100%;
}
td.has-toggle {
width: calc(20px + 0.3rem);
}
td.subgrid-parent {
padding: 0;
}
table tbody tr.selected td {
background-color: #00008b;
color: #fff;
}
button.toggle {
color: inherit;
padding: 0;
}
button.toggle :global(svg) {
width: 15px;
height: 15px;
vertical-align: top;
}
.right {
text-align: right;
}
</style>

View File

@ -0,0 +1,15 @@
<script>
export let name;
</script>
{#if name === 'radio'}
<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" class="feather feather-radio"><circle cx="12" cy="12" r="2"></circle><path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14"></path></svg>
{:else if name === 'chev-r'}
<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" class="feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg>
{:else if name === 'chev-d'}
<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" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>
{:else if name === 'db'}
<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" class="feather feather-database"><ellipse cx="12" cy="5" rx="9" ry="3"></ellipse><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path></svg>
{:else if name === 'x'}
<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" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
{/if}

View File

@ -0,0 +1,105 @@
<script>
import { fade, fly } from 'svelte/transition';
import Icon from './icon.svelte';
export let show = false;
export let title = undefined;
export let contentPadding = true;
</script>
{#if show}
<div class="modal outer" on:mousedown|self={close} transition:fade>
<div class="inner" transition:fly={{ y: 100 }}>
<header>
{#if title}
<div class="title">{title}</div>
{/if}
<button class="btn close" on:click={() => show = false} title="close">
<Icon name="x" />
</button>
</header>
<div class="slot content" class:p-0={!contentPadding}> <slot /> </div>
{#if $$slots.footerLeft || $$slots.footerRight}
<footer>
<slot name="footer" />
</footer>
{/if}
</div>
</div>
{/if}
<style>
.outer {
position: fixed;
display: flex;
z-index: 100;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
margin: 0;
padding-top: 1rem;
cursor: pointer;
}
.inner {
max-width: 80vw;
max-height: 80vh;
background-color: #fff;
margin-left: auto;
margin-right: auto;
margin-bottom: auto;
width: 100%;
border-radius: 10px;
display: flex;
flex-direction: column;
cursor: auto;
}
.inner > :global(*:first-child) {
margin-top: 0;
}
.inner > :global(*:last-child) {
margin-bottom: 0;
}
header {
border-bottom: 1px solid #ccc;
display: flex;
align-items: center;
padding: 1rem;
}
header .title {
font-size: 1.5rem;
}
.close {
margin-left: auto;
}
.content {
padding: 1rem;
overflow-y: auto;
max-height: 100%;
}
footer {
padding: 1rem;
border-top: 1px solid #ccc;
}
@media (max-width: 600px) {
.outer {
padding: 0;
}
.inner {
max-width: 100%;
max-height: 100%;
width: 100%;
margin-top: auto;
margin-bottom: 0;
}
}
</style>

View File

@ -0,0 +1,64 @@
<script>
import Grid from './grid.svelte';
export let data = [];
export let key = '_id';
export let showHeaders = false;
export let contained = true;
console.log(data);
let items = [];
$: if (data) {
items = [];
if (Array.isArray(data)) {
for (const item of data) {
const _item = {};
_item.key = item[key];
_item.children = dissectObject(item);
items = [ ...items, _item ];
}
}
else {
items = dissectObject(data);
console.log(items);
}
}
function getType(value) {
if (Array.isArray(value)) {
return `array (${value.length} item${value.length === 1 ? '' : 's'})`;
}
else if (new Date(value).toString() !== 'Invalid Date') {
return 'date';
}
else if (typeof value === 'object') {
const keys = Object.keys(value);
return `object (${keys.length} item${keys.length === 1 ? '' : 's'})`;
}
else {
return typeof value;
}
}
function dissectObject(object) {
return Object.entries(object).map(([ key, value ]) => {
const type = getType(value);
const child = { key, value, type };
if (type.startsWith('object')) {
child.children = dissectObject(value);
}
return child;
});
}
</script>
<Grid columns={[
{ key: 'key', label: 'Key' },
{ key: 'value', label: 'Value' },
{ key: 'type', label: 'Type' },
]} {items} {showHeaders} {contained} key="key" />

View File

@ -0,0 +1,54 @@
<script>
import { createEventDispatcher } from 'svelte';
export let tabs = [];
export let selectedKey = {};
const dispatch = createEventDispatcher();
function select(tabKey) {
selectedKey = tabKey;
dispatch('select', tabKey);
}
</script>
<nav class="tabs">
<ul>
{#each tabs as tab (tab.key)}
<li class="tab" class:active={tab.key === selectedKey}>
<button on:click={() => select(tab.key)}>{tab.title}</button>
</li>
{/each}
</ul>
</nav>
<style>
.tabs {
border-bottom: 1px solid #ccc;
padding: 0 0.5rem;
}
.tabs ul {
overflow-x: scroll;
display: flex;
gap: 0.5rem;
list-style: none;
}
.tabs li {
display: inline-block;
flex-grow: 1;
}
.tabs li button {
width: 100%;
padding: 0.7rem 1rem;
border: 1px solid #ccc;
border-bottom: none;
border-radius: 5px 5px 0 0;
cursor: pointer;
}
.tabs li.active button {
color: #fff;
background-color: #00008b;
border-color: #00008b;
cursor: not-allowed;
}
</style>

7
frontend/src/main.js Normal file
View File

@ -0,0 +1,7 @@
import './reset.css';
import './style.css';
import App from './app.svelte';
const app = new App({ target: document.getElementById('app') });
export default app;

View File

@ -0,0 +1,78 @@
<script>
import Modal from '../../components/modal.svelte';
import Icon from '../../components/icon.svelte';
import { createEventDispatcher } from 'svelte';
export let hosts = {};
export let activeHostKey = '';
export let modalOpen = false;
const dispatch = createEventDispatcher();
$: host = hosts?.[activeHostKey];
function select(hostKey) {
activeHostKey = hostKey;
dispatch('select', hostKey);
}
</script>
<div class="addressbar">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="address" class:empty={!host?.uri} on:click={() => modalOpen = true}>
{host?.uri || 'No host selected'}
</div>
<div class="actions">
<button class="btn" on:click={() => modalOpen = true}>
<Icon name="db" />
</button>
</div>
</div>
<Modal bind:show={modalOpen} title="Hosts">
{#if Object.keys(hosts).length}
<ul class="hosts">
{#each Object.entries(hosts) as [hostKey, host]}
<li>
<button on:click={() => select(hostKey)}>
{host.name}
</button>
</li>
{/each}
</ul>
{/if}
</Modal>
<style>
.addressbar {
display: flex;
justify-content: space-between;
align-items: center;
margin: 1rem;
padding: 0.5rem 0.5rem 0.5rem 1rem;
border: 1px solid #ccc;
border-radius: 10px;
}
.address.empty {
font-style: italic;
opacity: 0.6;
}
.hosts {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.5rem;
}
.hosts li button {
display: block;
width: 100%;
height: 100%;
padding: 1rem;
background-color: #ddd;
border-radius: 10px;
}
.hosts li button:hover {
background-color: #ccc;
}
</style>

View File

@ -0,0 +1,93 @@
<script>
import { PerformFind } from '../../../wailsjs/go/main/App';
import CodeExample from '../../components/code-example.svelte';
import { onMount } from 'svelte';
import { input } from '../../actions';
import ObjectGrid from '../../components/objectgrid.svelte';
export let collection;
const defaults = {
query: '{}',
sort: '{ "_id": 1 }',
fields: '{}',
skip: 0,
limit: 30,
};
const form = {
query: '{}',
sort: '{ "_id": 1 }',
fields: '{}',
skip: 0,
limit: 30,
};
let result = [];
let queryField;
$: code = `db.${collection.key}.find(${form.query || '{}'}${form.fields && form.fields !== '{}' ? `, ${form.fields}` : ''}).sort(${form.sort})${form.skip ? `.skip(${form.skip})` : ''}${form.limit ? `.limit(${form.limit})` : ''};`;
$: if (collection) {
result = [];
}
async function submitQuery() {
result = await PerformFind(collection.hostKey, collection.dbKey, collection.key, JSON.stringify(form));
}
onMount(() => {
queryField?.focus();
queryField?.select();
});
</script>
<form on:submit|preventDefault={submitQuery}>
<div class="row-one">
<label class="field">
<span class="label">Query or id</span>
<input type="text" class="code" bind:this={queryField} bind:value={form.query} use:input={{ json: true }} placeholder={defaults.query} />
</label>
<label class="field">
<span class="label">Sort</span>
<input type="text" class="code" bind:value={form.sort} use:input={{ json: true }} placeholder={defaults.sort} />
</label>
</div>
<div class="row-two">
<label class="field">
<span class="label">Fields</span>
<input type="text" class="code" bind:value={form.fields} use:input={{ json: true }} placeholder={defaults.fields} />
</label>
<label class="field">
<span class="label">Skip</span>
<input type="number" min="0" bind:value={form.skip} use:input placeholder={defaults.skip} />
</label>
<label class="field">
<span class="label">Limit</span>
<input type="number" min="0" bind:value={form.limit} use:input placeholder={defaults.limit} />
</label>
<button type="submit" class="btn">Run</button>
</div>
</form>
<CodeExample {code} />
<ObjectGrid data={result} />
<style>
.row-one {
display: grid;
gap: 0.5rem;
grid-template-columns: 3fr 2fr;
margin-bottom: 0.5rem;
}
.row-two {
display: grid;
gap: 0.5rem;
grid-template-columns: 5fr 1fr 1fr 1fr;
margin-bottom: 0.5rem;
}
</style>

View File

@ -0,0 +1,68 @@
<script>
import ObjectGrid from '../../components/objectgrid.svelte';
import CodeExample from '../../components/code-example.svelte';
import TabBar from '../../components/tabbar.svelte';
import Find from './find.svelte';
import Indexes from './indexes.svelte';
import Insert from './insert.svelte';
import Remove from './remove.svelte';
export let collection;
export let hostKey;
export let dbKey;
export let collectionKey;
let tab = 'find';
$: if (collection) {
collection.hostKey = hostKey;
collection.dbKey = dbKey;
collection.key = collectionKey;
}
</script>
<div class="collection" class:empty={!collection}>
{#if collection}
<TabBar tabs={[
{ key: 'stats', title: 'Stats' },
{ key: 'find', title: 'Find' },
{ key: 'insert', title: 'Insert' },
{ key: 'update', title: 'Update' },
{ key: 'remove', title: 'Remove' },
{ key: 'indexes', title: 'Indexes' },
]} bind:selectedKey={tab} />
<div class="container">
{#if tab === 'stats'}
<CodeExample code="db.stats()" />
<ObjectGrid data={collection.stats} />
{:else if tab === 'find'}
<Find {collection} />
{:else if tab === 'insert'}
<Insert {collection} />
{:else if tab === 'remove'}
<Remove {collection} />
{:else if tab === 'indexes'}
<Indexes {collection} />
{/if}
</div>
{:else}
No collection selected
{/if}
</div>
<style>
.collection {
margin: 1rem 1rem 1rem 0;
height: 100%;
display: flex;
flex-direction: column;
}
.container {
padding: 0.5rem 0.5rem 0;
flex: 1;
display: flex;
flex-direction: column;
}
</style>

View File

@ -0,0 +1,13 @@
<script>
export let collection;
const indexes = [];
function getIndexes() {}
</script>
<div class="buttons">
<button class="btn">Get indexes</button>
<button class="btn" disabled={!indexes?.length}>Drop selected</button>
<button class="btn">Create&hellip;</button>
</div>

View File

@ -0,0 +1,39 @@
<script>
import { PerformInsert } from '../../../wailsjs/go/main/App';
export let collection;
let input = '';
let insertedIds;
$: if (collection) {
insertedIds = undefined;
}
async function insert() {
insertedIds = await PerformInsert(collection.hostKey, collection.dbKey, collection.key, input);
}
</script>
<form on:submit|preventDefault={insert}>
<label class="field">
<textarea cols="30" rows="10" bind:value={input} placeholder="[]" class="code"></textarea>
</label>
<div class="flex">
<div>
{#if insertedIds}
Success! {insertedIds.length} document{insertedIds.length > 1 ? 's' : ''} inserted
{/if}
</div>
<button type="submit" class="btn">Insert</button>
</div>
</form>
<style>
.flex {
display: flex;
justify-content: space-between;
margin-top: 0.5rem;
}
</style>

View File

@ -0,0 +1,31 @@
<script>
import CodeExample from '../../components/code-example.svelte';
export let collection;
let remove = '';
$: code = `db.${collection.key}.remove(${remove});`;
function insert() {}
</script>
<form on:submit|preventDefault={insert}>
<CodeExample {code} />
<label class="field">
<textarea cols="30" rows="10" bind:value={remove} placeholder={'{}'} class="code"></textarea>
</label>
<div class="flex">
<div></div>
<button type="submit" class="btn">Remove</button>
</div>
</form>
<style>
.flex {
display: flex;
justify-content: space-between;
margin-top: 0.5rem;
}
</style>

66
frontend/src/reset.css Normal file
View File

@ -0,0 +1,66 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/* Buttons */
button {
font: inherit;
border: none;
background: none;
margin: 0;
padding: 0;
box-shadow: none;
cursor: pointer;
}
/* Inputs */
input {
font: inherit;
min-width: 100px;
width: 100%;
}

11
frontend/src/stores.js Normal file
View File

@ -0,0 +1,11 @@
import { writable } from 'svelte/store';
export const busy = writable(false);
busy.subscribe(isBusy => {
if (isBusy) {
document.body.classList.add('busy');
}
else {
document.body.classList.remove('busy');
}
});

93
frontend/src/style.css Normal file
View File

@ -0,0 +1,93 @@
html,
body {
height: 100vh;
max-height: 100vh;
overflow: hidden;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
margin: 0;
padding: 0;
-webkit-user-select: none;
user-select: none;
cursor: default;
font-size: 15px;
line-height: 15px;
}
* {
vertical-align: middle;
box-sizing: border-box;
}
p {
margin: 0 0 0.7rem 0;
}
.loading {
cursor: wait;
}
.field {
display: flex;
white-space: nowrap;
align-items: stretch;
}
.field > * {
padding: 0.5rem;
border: 1px solid #ccc;
border-right: none;
display: inline-block;
margin: 0;
}
.field .label {
background-color: #eee;
display: flex;
align-items: center;
}
.field > :first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
.field > :last-child {
border-right: 1px solid #ccc;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.field > input,
.field > textarea {
flex: 1;
}
.field > textarea:focus,
.field > input:focus {
outline: none;
border-color: #00008b;
box-shadow: 0 0 0 3px rgba(0, 0, 139, 0.2);
}
.field > input.invalid,
.field > textarea.invalid {
background-color: rgba(255, 80, 80, 0.3);
border-color: rgb(255, 80, 80);
}
.btn {
background-color: #00008b;
padding: 0.5rem;
border-radius: 10px;
color: #fff;
}
.btn:focus {
box-shadow: 0 0 0 3px rgba(0, 0, 139, 0.2);
outline: none;
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn svg {
height: 15px;
width: auto;
vertical-align: bottom;
}
code, .code {
font-family: Menlo, monospace;
}

2
frontend/src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
/// <reference types="svelte" />
/// <reference types="vite/client" />

7
frontend/vite.config.js Normal file
View File

@ -0,0 +1,7 @@
import {defineConfig} from 'vite'
import {svelte} from '@sveltejs/vite-plugin-svelte'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [svelte()]
})

16
frontend/wailsjs/go/main/App.d.ts vendored Executable file
View File

@ -0,0 +1,16 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {map[string]main} from '../models';
import {primitive} from '../models';
export function Hosts():Promise<map[string]main.Host>;
export function OpenCollection(arg1:string,arg2:string,arg3:string):Promise<primitive.M>;
export function OpenConnection(arg1:string):Promise<Array<string>>;
export function OpenDatabase(arg1:string,arg2:string):Promise<Array<string>>;
export function PerformFind(arg1:string,arg2:string,arg3:string,arg4:string):Promise<any>;
export function PerformInsert(arg1:string,arg2:string,arg3:string,arg4:string):Promise<any>;

27
frontend/wailsjs/go/main/App.js Executable file
View File

@ -0,0 +1,27 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function Hosts() {
return window['go']['main']['App']['Hosts']();
}
export function OpenCollection(arg1, arg2, arg3) {
return window['go']['main']['App']['OpenCollection'](arg1, arg2, arg3);
}
export function OpenConnection(arg1) {
return window['go']['main']['App']['OpenConnection'](arg1);
}
export function OpenDatabase(arg1, arg2) {
return window['go']['main']['App']['OpenDatabase'](arg1, arg2);
}
export function PerformFind(arg1, arg2, arg3, arg4) {
return window['go']['main']['App']['PerformFind'](arg1, arg2, arg3, arg4);
}
export function PerformInsert(arg1, arg2, arg3, arg4) {
return window['go']['main']['App']['PerformInsert'](arg1, arg2, arg3, arg4);
}

View File

@ -0,0 +1,24 @@
{
"name": "@wailsapp/runtime",
"version": "2.0.0",
"description": "Wails Javascript runtime library",
"main": "runtime.js",
"types": "runtime.d.ts",
"scripts": {
},
"repository": {
"type": "git",
"url": "git+https://github.com/wailsapp/wails.git"
},
"keywords": [
"Wails",
"Javascript",
"Go"
],
"author": "Lea Anthony <lea.anthony@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/wailsapp/wails/issues"
},
"homepage": "https://github.com/wailsapp/wails#readme"
}

227
frontend/wailsjs/runtime/runtime.d.ts vendored Normal file
View File

@ -0,0 +1,227 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
export interface Position {
x: number;
y: number;
}
export interface Size {
w: number;
h: number;
}
export interface Screen {
isCurrent: boolean;
isPrimary: boolean;
width : number
height : number
}
// Environment information such as platform, buildtype, ...
export interface EnvironmentInfo {
buildType: string;
platform: string;
arch: string;
}
// [EventsEmit](https://wails.io/docs/reference/runtime/events#eventsemit)
// emits the given event. Optional data may be passed with the event.
// This will trigger any event listeners.
export function EventsEmit(eventName: string, ...data: any): void;
// [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name.
export function EventsOn(eventName: string, callback: (...data: any) => void): () => void;
// [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple)
// sets up a listener for the given event name, but will only trigger a given number times.
export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void;
// [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce)
// sets up a listener for the given event name, but will only trigger once.
export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void;
// [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff)
// unregisters the listener for the given event name.
export function EventsOff(eventName: string, ...additionalEventNames: string[]): void;
// [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall)
// unregisters all listeners.
export function EventsOffAll(): void;
// [LogPrint](https://wails.io/docs/reference/runtime/log#logprint)
// logs the given message as a raw message
export function LogPrint(message: string): void;
// [LogTrace](https://wails.io/docs/reference/runtime/log#logtrace)
// logs the given message at the `trace` log level.
export function LogTrace(message: string): void;
// [LogDebug](https://wails.io/docs/reference/runtime/log#logdebug)
// logs the given message at the `debug` log level.
export function LogDebug(message: string): void;
// [LogError](https://wails.io/docs/reference/runtime/log#logerror)
// logs the given message at the `error` log level.
export function LogError(message: string): void;
// [LogFatal](https://wails.io/docs/reference/runtime/log#logfatal)
// logs the given message at the `fatal` log level.
// The application will quit after calling this method.
export function LogFatal(message: string): void;
// [LogInfo](https://wails.io/docs/reference/runtime/log#loginfo)
// logs the given message at the `info` log level.
export function LogInfo(message: string): void;
// [LogWarning](https://wails.io/docs/reference/runtime/log#logwarning)
// logs the given message at the `warning` log level.
export function LogWarning(message: string): void;
// [WindowReload](https://wails.io/docs/reference/runtime/window#windowreload)
// Forces a reload by the main application as well as connected browsers.
export function WindowReload(): void;
// [WindowReloadApp](https://wails.io/docs/reference/runtime/window#windowreloadapp)
// Reloads the application frontend.
export function WindowReloadApp(): void;
// [WindowSetAlwaysOnTop](https://wails.io/docs/reference/runtime/window#windowsetalwaysontop)
// Sets the window AlwaysOnTop or not on top.
export function WindowSetAlwaysOnTop(b: boolean): void;
// [WindowSetSystemDefaultTheme](https://wails.io/docs/next/reference/runtime/window#windowsetsystemdefaulttheme)
// *Windows only*
// Sets window theme to system default (dark/light).
export function WindowSetSystemDefaultTheme(): void;
// [WindowSetLightTheme](https://wails.io/docs/next/reference/runtime/window#windowsetlighttheme)
// *Windows only*
// Sets window to light theme.
export function WindowSetLightTheme(): void;
// [WindowSetDarkTheme](https://wails.io/docs/next/reference/runtime/window#windowsetdarktheme)
// *Windows only*
// Sets window to dark theme.
export function WindowSetDarkTheme(): void;
// [WindowCenter](https://wails.io/docs/reference/runtime/window#windowcenter)
// Centers the window on the monitor the window is currently on.
export function WindowCenter(): void;
// [WindowSetTitle](https://wails.io/docs/reference/runtime/window#windowsettitle)
// Sets the text in the window title bar.
export function WindowSetTitle(title: string): void;
// [WindowFullscreen](https://wails.io/docs/reference/runtime/window#windowfullscreen)
// Makes the window full screen.
export function WindowFullscreen(): void;
// [WindowUnfullscreen](https://wails.io/docs/reference/runtime/window#windowunfullscreen)
// Restores the previous window dimensions and position prior to full screen.
export function WindowUnfullscreen(): void;
// [WindowIsFullscreen](https://wails.io/docs/reference/runtime/window#windowisfullscreen)
// Returns the state of the window, i.e. whether the window is in full screen mode or not.
export function WindowIsFullscreen(): Promise<boolean>;
// [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize)
// Sets the width and height of the window.
export function WindowSetSize(width: number, height: number): Promise<Size>;
// [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize)
// Gets the width and height of the window.
export function WindowGetSize(): Promise<Size>;
// [WindowSetMaxSize](https://wails.io/docs/reference/runtime/window#windowsetmaxsize)
// Sets the maximum window size. Will resize the window if the window is currently larger than the given dimensions.
// Setting a size of 0,0 will disable this constraint.
export function WindowSetMaxSize(width: number, height: number): void;
// [WindowSetMinSize](https://wails.io/docs/reference/runtime/window#windowsetminsize)
// Sets the minimum window size. Will resize the window if the window is currently smaller than the given dimensions.
// Setting a size of 0,0 will disable this constraint.
export function WindowSetMinSize(width: number, height: number): void;
// [WindowSetPosition](https://wails.io/docs/reference/runtime/window#windowsetposition)
// Sets the window position relative to the monitor the window is currently on.
export function WindowSetPosition(x: number, y: number): void;
// [WindowGetPosition](https://wails.io/docs/reference/runtime/window#windowgetposition)
// Gets the window position relative to the monitor the window is currently on.
export function WindowGetPosition(): Promise<Position>;
// [WindowHide](https://wails.io/docs/reference/runtime/window#windowhide)
// Hides the window.
export function WindowHide(): void;
// [WindowShow](https://wails.io/docs/reference/runtime/window#windowshow)
// Shows the window, if it is currently hidden.
export function WindowShow(): void;
// [WindowMaximise](https://wails.io/docs/reference/runtime/window#windowmaximise)
// Maximises the window to fill the screen.
export function WindowMaximise(): void;
// [WindowToggleMaximise](https://wails.io/docs/reference/runtime/window#windowtogglemaximise)
// Toggles between Maximised and UnMaximised.
export function WindowToggleMaximise(): void;
// [WindowUnmaximise](https://wails.io/docs/reference/runtime/window#windowunmaximise)
// Restores the window to the dimensions and position prior to maximising.
export function WindowUnmaximise(): void;
// [WindowIsMaximised](https://wails.io/docs/reference/runtime/window#windowismaximised)
// Returns the state of the window, i.e. whether the window is maximised or not.
export function WindowIsMaximised(): Promise<boolean>;
// [WindowMinimise](https://wails.io/docs/reference/runtime/window#windowminimise)
// Minimises the window.
export function WindowMinimise(): void;
// [WindowUnminimise](https://wails.io/docs/reference/runtime/window#windowunminimise)
// Restores the window to the dimensions and position prior to minimising.
export function WindowUnminimise(): void;
// [WindowIsMinimised](https://wails.io/docs/reference/runtime/window#windowisminimised)
// Returns the state of the window, i.e. whether the window is minimised or not.
export function WindowIsMinimised(): Promise<boolean>;
// [WindowIsNormal](https://wails.io/docs/reference/runtime/window#windowisnormal)
// Returns the state of the window, i.e. whether the window is normal or not.
export function WindowIsNormal(): Promise<boolean>;
// [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour)
// Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels.
export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void;
// [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall)
// Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system.
export function ScreenGetAll(): Promise<Screen[]>;
// [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl)
// Opens the given URL in the system browser.
export function BrowserOpenURL(url: string): void;
// [Environment](https://wails.io/docs/reference/runtime/intro#environment)
// Returns information about the environment
export function Environment(): Promise<EnvironmentInfo>;
// [Quit](https://wails.io/docs/reference/runtime/intro#quit)
// Quits the application.
export function Quit(): void;
// [Hide](https://wails.io/docs/reference/runtime/intro#hide)
// Hides the application.
export function Hide(): void;
// [Show](https://wails.io/docs/reference/runtime/intro#show)
// Shows the application.
export function Show(): void;

View File

@ -0,0 +1,194 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
export function LogPrint(message) {
window.runtime.LogPrint(message);
}
export function LogTrace(message) {
window.runtime.LogTrace(message);
}
export function LogDebug(message) {
window.runtime.LogDebug(message);
}
export function LogInfo(message) {
window.runtime.LogInfo(message);
}
export function LogWarning(message) {
window.runtime.LogWarning(message);
}
export function LogError(message) {
window.runtime.LogError(message);
}
export function LogFatal(message) {
window.runtime.LogFatal(message);
}
export function EventsOnMultiple(eventName, callback, maxCallbacks) {
window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks);
}
export function EventsOn(eventName, callback) {
EventsOnMultiple(eventName, callback, -1);
}
export function EventsOff(eventName, ...additionalEventNames) {
return window.runtime.EventsOff(eventName, ...additionalEventNames);
}
export function EventsOnce(eventName, callback) {
EventsOnMultiple(eventName, callback, 1);
}
export function EventsEmit(eventName) {
let args = [eventName].slice.call(arguments);
return window.runtime.EventsEmit.apply(null, args);
}
export function WindowReload() {
window.runtime.WindowReload();
}
export function WindowReloadApp() {
window.runtime.WindowReloadApp();
}
export function WindowSetAlwaysOnTop(b) {
window.runtime.WindowSetAlwaysOnTop(b);
}
export function WindowSetSystemDefaultTheme() {
window.runtime.WindowSetSystemDefaultTheme();
}
export function WindowSetLightTheme() {
window.runtime.WindowSetLightTheme();
}
export function WindowSetDarkTheme() {
window.runtime.WindowSetDarkTheme();
}
export function WindowCenter() {
window.runtime.WindowCenter();
}
export function WindowSetTitle(title) {
window.runtime.WindowSetTitle(title);
}
export function WindowFullscreen() {
window.runtime.WindowFullscreen();
}
export function WindowUnfullscreen() {
window.runtime.WindowUnfullscreen();
}
export function WindowIsFullscreen() {
return window.runtime.WindowIsFullscreen();
}
export function WindowGetSize() {
return window.runtime.WindowGetSize();
}
export function WindowSetSize(width, height) {
window.runtime.WindowSetSize(width, height);
}
export function WindowSetMaxSize(width, height) {
window.runtime.WindowSetMaxSize(width, height);
}
export function WindowSetMinSize(width, height) {
window.runtime.WindowSetMinSize(width, height);
}
export function WindowSetPosition(x, y) {
window.runtime.WindowSetPosition(x, y);
}
export function WindowGetPosition() {
return window.runtime.WindowGetPosition();
}
export function WindowHide() {
window.runtime.WindowHide();
}
export function WindowShow() {
window.runtime.WindowShow();
}
export function WindowMaximise() {
window.runtime.WindowMaximise();
}
export function WindowToggleMaximise() {
window.runtime.WindowToggleMaximise();
}
export function WindowUnmaximise() {
window.runtime.WindowUnmaximise();
}
export function WindowIsMaximised() {
return window.runtime.WindowIsMaximised();
}
export function WindowMinimise() {
window.runtime.WindowMinimise();
}
export function WindowUnminimise() {
window.runtime.WindowUnminimise();
}
export function WindowSetBackgroundColour(R, G, B, A) {
window.runtime.WindowSetBackgroundColour(R, G, B, A);
}
export function ScreenGetAll() {
return window.runtime.ScreenGetAll();
}
export function WindowIsMinimised() {
return window.runtime.WindowIsMinimised();
}
export function WindowIsNormal() {
return window.runtime.WindowIsNormal();
}
export function BrowserOpenURL(url) {
window.runtime.BrowserOpenURL(url);
}
export function Environment() {
return window.runtime.Environment();
}
export function Quit() {
window.runtime.Quit();
}
export function Hide() {
window.runtime.Hide();
}
export function Show() {
window.runtime.Show();
}