feat: remove old site UI, replace with ktb
This commit is contained in:
parent
93b74d0179
commit
d01128d54f
72 changed files with 772 additions and 31005 deletions
98
apps/ktb-static/script.js
Normal file
98
apps/ktb-static/script.js
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
// script.js
|
||||
document.addEventListener("alpine:init", () => {
|
||||
Alpine.store("dictionary", {
|
||||
search_results: {
|
||||
terms: 0,
|
||||
results: [
|
||||
{
|
||||
lemma_name: "wiigel",
|
||||
word_forms: [
|
||||
{ word_form_id: 1262, word_form: "wijgeu", lect: { name: "ArekaDareka" } },
|
||||
{ word_form_id: 1263, word_form: "wigl", lect: { name: "Anott" } },
|
||||
{ word_form_id: 1264, word_form: "wijev", lect: { name: "Djin" } }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
prefs: {
|
||||
colorSidebarBg: "#000",
|
||||
colorMainBg: "#eee",
|
||||
colorCardBg: "#fff",
|
||||
colorPrimary: "#0bf",
|
||||
colorSecondary: "#08e",
|
||||
colorText: "#333",
|
||||
colorTextSecondary: "#555",
|
||||
spaceMd: 1.5,
|
||||
shadowStrength: 0.1
|
||||
},
|
||||
|
||||
updateCSSVariables() {
|
||||
document.documentElement.style.setProperty("--color-sidebar-bg", this.prefs.colorSidebarBg);
|
||||
document.documentElement.style.setProperty("--color-main-bg", this.prefs.colorMainBg);
|
||||
document.documentElement.style.setProperty("--color-card-bg", this.prefs.colorCardBg);
|
||||
document.documentElement.style.setProperty("--color-primary", this.prefs.colorPrimary);
|
||||
document.documentElement.style.setProperty("--color-secondary", this.prefs.colorSecondary);
|
||||
document.documentElement.style.setProperty("--color-text", this.prefs.colorText);
|
||||
document.documentElement.style.setProperty("--color-text-secondary", this.prefs.colorTextSecondary);
|
||||
document.documentElement.style.setProperty("--space-md", `${this.prefs.spaceMd}rem`);
|
||||
document.documentElement.style.setProperty("--color-shadow", `rgba(0, 0, 0, ${this.prefs.shadowStrength})`);
|
||||
},
|
||||
|
||||
loadPreferences() {
|
||||
const saved_prefs = localStorage.getItem("prefs");
|
||||
if (saved_prefs) {
|
||||
this.prefs = { ...this.prefs, ...JSON.parse(saved_prefs) };
|
||||
this.updateCSSVariables();
|
||||
}
|
||||
},
|
||||
|
||||
savePreferences() {
|
||||
localStorage.setItem("prefs", JSON.stringify(this.prefs));
|
||||
this.updateCSSVariables();
|
||||
},
|
||||
|
||||
async fetchAllTerms(searchTerm) {
|
||||
try {
|
||||
const res = await axios.get('http://localhost:1225/search', {
|
||||
params: { search_term: searchTerm }
|
||||
});
|
||||
this.search_results = res.data;
|
||||
} catch (e) { console.error(e); }
|
||||
},
|
||||
|
||||
async fetchOneLemmaDetail(lemma_name) {
|
||||
try {
|
||||
const res = await axios.get('http://localhost:1225/lemma-detail', {
|
||||
params: { lemma_name }
|
||||
});
|
||||
return res.data.lemma_detail;
|
||||
} catch (e) { console.error(e); }
|
||||
},
|
||||
|
||||
async postDefinition(definition_text, lemma_name) {
|
||||
try {
|
||||
const res = await axios.put('http://localhost:1225/definition', {
|
||||
definition_text, lemma_name
|
||||
});
|
||||
return res.data.lemma_detail;
|
||||
} catch (e) { console.error(e); }
|
||||
},
|
||||
|
||||
async postExample(example_text, lemma_name) {
|
||||
try {
|
||||
const res = await axios.put('http://localhost:1225/example', {
|
||||
example_text, lemma_name
|
||||
});
|
||||
return res.data.lemma_detail;
|
||||
} catch (e) { console.error(e); }
|
||||
},
|
||||
|
||||
filterResults(searchTerm) {
|
||||
if (!searchTerm) return this.search_results.results;
|
||||
return this.search_results.results.filter(item =>
|
||||
item.lemma_name.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
258
apps/ktb-static/search.html
Normal file
258
apps/ktb-static/search.html
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Dictionary Cards</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|
||||
<script src="https://unpkg.com/alpinejs" defer></script>
|
||||
<script src="script.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
class="app-container"
|
||||
x-data="{
|
||||
searchTerm: '',
|
||||
showPrefs: false
|
||||
}"
|
||||
@alpine:init="$store.dictionary.loadPreferences()">
|
||||
<aside class="sidebar">
|
||||
<div class="preferences-pane">
|
||||
<button
|
||||
@click="showPrefs = !showPrefs"
|
||||
class="prefs-toggle">
|
||||
Preferences
|
||||
</button>
|
||||
|
||||
<div class="prefs-content" x-show="showPrefs">
|
||||
<div class="prefs-section">
|
||||
<h3>Sejenazma</h3>
|
||||
<label>
|
||||
Hinafarge fu ljevatel:
|
||||
<input
|
||||
type="color"
|
||||
x-model="$store.dictionary.prefs.colorSidebarBg"
|
||||
@input="$store.dictionary.savePreferences()" />
|
||||
</label>
|
||||
<label>
|
||||
Hinafarge fu glavna lehtia:
|
||||
<input
|
||||
type="color"
|
||||
x-model="$store.dictionary.prefs.colorMainBg"
|
||||
@input="$store.dictionary.savePreferences()" />
|
||||
</label>
|
||||
<label>
|
||||
Hinafarge fu kofuga:
|
||||
<input
|
||||
type="color"
|
||||
x-model="$store.dictionary.prefs.colorCardBg"
|
||||
@input="$store.dictionary.savePreferences()" />
|
||||
</label>
|
||||
<label>
|
||||
Viktifarge:
|
||||
<input
|
||||
type="color"
|
||||
x-model="$store.dictionary.prefs.colorPrimary"
|
||||
@input="$store.dictionary.savePreferences()" />
|
||||
</label>
|
||||
<label>
|
||||
Nisvikti farge:
|
||||
<input
|
||||
type="color"
|
||||
x-model="$store.dictionary.prefs.colorSecondary"
|
||||
@input="$store.dictionary.savePreferences()" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="prefs-section">
|
||||
<h3>Mellanluft</h3>
|
||||
<label>
|
||||
Card Gap:
|
||||
<input
|
||||
type="range"
|
||||
x-model.number="$store.dictionary.prefs.spaceMd"
|
||||
min="0.5"
|
||||
max="3"
|
||||
step="0.1"
|
||||
@input="$store.dictionary.savePreferences()" />
|
||||
<span
|
||||
x-text="$store.dictionary.prefs.spaceMd + 'rem'"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="prefs-section">
|
||||
<h3>Kurai</h3>
|
||||
<label>
|
||||
Shadow Strength:
|
||||
<input
|
||||
type="range"
|
||||
x-model.number="$store.dictionary.prefs.shadowStrength"
|
||||
min="0.1"
|
||||
max="0.5"
|
||||
step="0.05"
|
||||
@input="$store.dictionary.savePreferences()" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="main-content">
|
||||
<div class="search-bar">
|
||||
<button
|
||||
@click="$store.dictionary.fetchAllTerms(searchTerm)">
|
||||
Zuha
|
||||
</button>
|
||||
<input
|
||||
type="text"
|
||||
x-model="searchTerm"
|
||||
@keydown.enter="$store.dictionary.fetchAllTerms(searchTerm)"
|
||||
placeholder="Tasta ko(tel) her..." />
|
||||
</div>
|
||||
<div class="cards-container">
|
||||
<template
|
||||
x-for="item in $store.dictionary.filterResults(searchTerm)"
|
||||
:key="item.lemma_name">
|
||||
<div
|
||||
class="card"
|
||||
@click.self="toggle_expand()"
|
||||
x-data="{
|
||||
expanded: false,
|
||||
adding_definition: false,
|
||||
adding_example: false,
|
||||
adding_media: false,
|
||||
example_text: '',
|
||||
definition_text: '',
|
||||
lemma_detail: null,
|
||||
async toggle_expand() {
|
||||
if(!this.lemma_detail){
|
||||
/* If there isn't a lemma detail loaded yet, get it then expand. */
|
||||
this.lemma_detail = await $store.dictionary.fetchOneLemmaDetail(item.lemma_name);
|
||||
|
||||
if(this.lemma_detail) {
|
||||
this.expanded = true;
|
||||
} else {
|
||||
alert(`Failed to load lemma ${item.lemma_name}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* If there is content, simply toggle the state of the card */
|
||||
this.expanded = !this.expanded;
|
||||
},
|
||||
get_lemma_detail(){
|
||||
return this.lemma_detail;
|
||||
}
|
||||
}">
|
||||
<h3 x-text="item.lemma_name"></h3>
|
||||
<p>
|
||||
<template x-for="wf in item.word_forms">
|
||||
<span
|
||||
x-text="wf.word_form + ' '"
|
||||
:title="wf.lect.name">
|
||||
</span>
|
||||
</template>
|
||||
</p>
|
||||
|
||||
<button
|
||||
class="is-fullwidth dropdown"
|
||||
@click.self="toggle_expand()">
|
||||
<span x-show="!expanded">v</span>
|
||||
<span x-show="expanded">^</span>
|
||||
</button>
|
||||
|
||||
<div x-show="expanded && lemma_detail">
|
||||
<h4>Trofal</h4>
|
||||
<table class="is-fullwidth">
|
||||
<template
|
||||
x-for="wf2 in (get_lemma_detail()?.word_forms ?? [])">
|
||||
<tr x-data="{editing:false}">
|
||||
<td
|
||||
x-text="wf2 ? wf2.lect.name : '(empty)'"></td>
|
||||
<td>
|
||||
<span x-text="wf2 ? wf2.word_form : '(empty)'"
|
||||
@click="editing = true"
|
||||
x-show="!editing"
|
||||
></span>
|
||||
|
||||
<input type="text" x-show="editing" x-model="wf2.word_form">
|
||||
</td>
|
||||
<td>
|
||||
<button class="float-left tight" x-show="!editing" @click="editing = true">~</button>
|
||||
<button class="float-left tight" x-show="editing" @click="editing = false">-></button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</table>
|
||||
|
||||
<h4>Imi</h4>
|
||||
<ul>
|
||||
<template
|
||||
x-for="def in (get_lemma_detail()?.definitions ?? [])"
|
||||
x-data="{
|
||||
editing:false
|
||||
}">
|
||||
<li><span x-show="!editing" x-text="def.definition_text"></span>
|
||||
<button class="float-right tight edit" x-show="!editing" @click="editing = true">~</button>
|
||||
<button class="float-right tight" x-show="editing" @click="editing = false">-></button>
|
||||
<input type="text" x-show="editing" x-model="def.definition_text">
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
<textarea
|
||||
class="is-fullwidth"
|
||||
x-show="adding_definition"
|
||||
placeholder="Tasta imi..."
|
||||
x-model="definition_text"></textarea>
|
||||
<button
|
||||
class="is-fullwidth"
|
||||
x-show="adding_definition"
|
||||
@click="$store.dictionary.postDefinition(definition_text, get_lemma_detail()?.lemma_name)">
|
||||
Popoczta
|
||||
</button>
|
||||
<button
|
||||
class="is-fullwidth"
|
||||
x-show="!adding_definition"
|
||||
@click="adding_definition = !adding_definition">
|
||||
+
|
||||
</button>
|
||||
|
||||
<h4>Tatoeba</h4>
|
||||
<ul>
|
||||
<template
|
||||
x-for="ex in (get_lemma_detail()?.examples ?? [])"
|
||||
x-data="{editing:false}">
|
||||
<li>
|
||||
<span x-show="!editing" x-text="ex.example_text"></span>
|
||||
<button class="float-right tight edit" x-show="!editing" @click="editing = true">~</button>
|
||||
<button class="float-right tight" x-show="editing" @click="editing = false">-></button>
|
||||
<input type="text" x-show="editing" x-model="ex.example_text">
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
<textarea
|
||||
class="is-fullwidth"
|
||||
x-show="adding_example"
|
||||
placeholder="Tasta tatoeba..."
|
||||
x-model="example_text"></textarea>
|
||||
<button
|
||||
class="is-fullwidth"
|
||||
x-show="adding_example"
|
||||
@click="$store.dictionary.postExample(example_text, get_lemma_detail()?.lemma_name)">
|
||||
Popoczta
|
||||
</button>
|
||||
<button
|
||||
class="is-fullwidth"
|
||||
x-show="!adding_example"
|
||||
@click="adding_example = !adding_example">
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
234
apps/ktb-static/styles.css
Normal file
234
apps/ktb-static/styles.css
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
:root {
|
||||
--color-sidebar-bg: #000;
|
||||
--color-primary: #0bf;
|
||||
--color-secondary: #08e;
|
||||
--color-edit: #fb0;
|
||||
--color-main-bg: #eee;
|
||||
--color-card-bg: #fff;
|
||||
--color-text: #333;
|
||||
--color-text-secondary: #555;
|
||||
--color-shadow: rgba(0, 0, 0, 0.1);
|
||||
--color-border: #ddd;
|
||||
|
||||
--space-xs: 0.2rem;
|
||||
--space-sm: 0.5rem;
|
||||
--space-md: 1.0rem;
|
||||
--space-lg: 1.5rem;
|
||||
--space-xl: 2rem;
|
||||
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 12px;
|
||||
|
||||
--shadow-sm: 0 1px 2px var(--color-shadow);
|
||||
--shadow-md: 0 2px 4px var(--color-shadow);
|
||||
--shadow-lg: 0 4px 8px var(--color-shadow);
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background-color: var(--color-main-bg);
|
||||
color: var(--color-text);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
button {
|
||||
border: solid 1px #fff;
|
||||
padding: var(--space-sm) var(--space-md);
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-primary);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: var(--color-secondary);
|
||||
}
|
||||
|
||||
li {
|
||||
margin-left: var(--space-md);
|
||||
}
|
||||
|
||||
.float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.float-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.tight {
|
||||
padding: 0 var(--space-md);
|
||||
margin: var(--space-xs);
|
||||
}
|
||||
|
||||
.edit {
|
||||
background-color: #fb0;
|
||||
}
|
||||
|
||||
.edit:hover {
|
||||
background-color: #bb0;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
border-radius: 0px;
|
||||
border-style: none;
|
||||
border-top: solid 1px var(--color-text);
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
border-radius: var(--radius-sm);
|
||||
background-color: transparent;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Main Content */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: var(--space-md);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-md);
|
||||
}
|
||||
|
||||
.is-fullwidth {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
.app-container {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
width: clamp(180px, 20%, 250px);
|
||||
background-color: var(--color-sidebar-bg);
|
||||
color: white;
|
||||
padding: var(--space-md);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-md);
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
/* Preferences Pane */
|
||||
.preferences-pane {
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.2);
|
||||
padding-top: var(--space-md);
|
||||
}
|
||||
|
||||
.prefs-toggle {
|
||||
background: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
color: white;
|
||||
width: 100%;
|
||||
padding: var(--space-xs);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.prefs-content {
|
||||
flex-direction: column;
|
||||
gap: var(--space-sm);
|
||||
margin-top: var(--space-sm);
|
||||
}
|
||||
|
||||
.prefs-content.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.prefs-section {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: var(--space-sm);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
.prefs-section h3 {
|
||||
margin-bottom: var(--space-xs);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.prefs-section label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-xs);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* Search Bar */
|
||||
.search-bar input {
|
||||
display: inline-block;
|
||||
width: 80%;
|
||||
padding: var(--space-sm) var(--space-md);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 1rem;
|
||||
transition: border-color 0.05s ease;
|
||||
}
|
||||
|
||||
.search-bar input:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.search-bar button {
|
||||
display: inline-block;
|
||||
background: var(--color-secondary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.search-bar button:hover {
|
||||
background: #000;
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.cards-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: var(--space-md);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card {
|
||||
align-self: start;
|
||||
background: var(--color-card-bg);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-md);
|
||||
box-shadow: var(--shadow-md);
|
||||
transition: transform 0.05s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
outline: 1px solid var(--color-primary);
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
font-size: clamp(1.1rem, 1.5vw, 1.25rem);
|
||||
margin-bottom: var(--space-xs);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.card h4 {
|
||||
font-size: clamp(1.rem, 1.4vw, 1.1rem);
|
||||
margin-top: var(--space-md);
|
||||
margin-bottom: var(--space-xs);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.card p {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue