Compare commits

..

1 Commits

Author SHA1 Message Date
Greg Shuflin
aba286cc4e Stupid change 2023-12-11 20:52:14 -08:00
10 changed files with 492 additions and 730 deletions

1
.gitignore vendored
View File

@ -9,4 +9,3 @@ node_modules/
!.yarn/versions
.parcel-cache
.aider*

12
flake.lock generated
View File

@ -5,11 +5,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"lastModified": 1692799911,
"narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44",
"type": "github"
},
"original": {
@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1743964447,
"narHash": "sha256-nEo1t3Q0F+0jQ36HJfbJtiRU4OI+/0jX/iITURKe3EE=",
"lastModified": 1693250523,
"narHash": "sha256-y3up5gXMTbnCsXrNEB5j+7TVantDLUYyQLu/ueiXuyg=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "063dece00c5a77e4a0ea24e5e5a5bd75232806f8",
"rev": "3efb0f6f404ec8dae31bdb1a9b17705ce0d6986e",
"type": "github"
},
"original": {

View File

@ -1,42 +1,40 @@
{
description = "Kucinako Wordbook of Arzhanai languages";
description = "Kucinako - Wordbook of Arzhanai languages";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = {
self,
nixpkgs,
flake-utils,
}:
flake-utils.lib.eachDefaultSystem (
system: let
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
frontend = pkgs.stdenv.mkDerivation (finalAttrs: {
pname = "kucinako";
version = "0.1";
node-modules = pkgs.mkYarnPackage {
name = "node-modules";
src = ./.;
yarnOfflineCache = pkgs.fetchYarnDeps {
yarnLock = finalAttrs.src + "/yarn.lock";
hash = "sha256-g5g2xlwDxH8O8zaLJ4meO1+DQdJIomVPqd6RXTAhDuE=";
};
nativeBuildInputs = with pkgs; [
yarnConfigHook
yarnBuildHook
# Needed for executing package.json scripts
nodejs
];
installPhase = ''
mkdir $out
mv dist/* $out
'';
});
in {
packages = {
default = frontend;
};
}
frontend = pkgs.stdenv.mkDerivation {
name = "frontend";
src = ./.;
buildInputs = [pkgs.yarn node-modules pkgs.lmdb];
buildPhase = ''
ln -s ${node-modules}/libexec/kucinako/node_modules node_modules
${pkgs.yarn}/bin/yarn build
'';
installPhase = ''
mkdir $out
mv dist $out/dist
'';
};
in
{
packages = {
node-modules = node-modules;
default = frontend;
};
}
);
}

View File

@ -8,6 +8,7 @@
<link rel="shortcut icon" href="./favicon.png" />
</head>
<body>
YOLO SWAGG
<div id="root"></div>
<script type="module" src="./index.js"></script>
</body>

View File

@ -1,9 +1,5 @@
default:
just --list
# Run local instance
run-local:
yarn start
copy-to-marjvena:
rsync --progress -r dist greg@marjvena.lan:/home/greg/

View File

@ -1,18 +1,7 @@
// Color variables
$background-color: #f0f0b8;
$primary-color: #b2a336;
$primary-color-dark: #a69530;
$text-color: #333;
$accent-color: #a63333;
$accent-color-dark: #6a3131;
$white: #fff;
body {
background-color: $background-color;
background-color: #f0f0b8;
font-size: 18pt;
font-family: "Biwa";
margin: 0;
padding: 0;
}
input {
@ -21,13 +10,11 @@ input {
main {
text-align: center;
padding: 1rem;
}
div .container {
max-width: 62rem;
margin: auto;
padding: 0 1rem;
}
div .results {
@ -35,74 +22,30 @@ div .results {
}
div .textInput {
max-width: 100%;
max-width: 70%;
margin: auto;
font-size: 22px;
}
input {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid $primary-color;
border-radius: 4px;
}
.searchDropdown {
font-size: 22px;
font-family: "Biwa";
padding: 10px 15px;
border: 2px solid $primary-color;
border-radius: 8px;
background-color: $white;
color: $text-color;
cursor: pointer;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
$encoded-primary-color: str-slice("#{$primary-color}", 2);
background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23#{$encoded-primary-color}%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E");
background-repeat: no-repeat;
background-position: right 15px top 50%;
background-size: 12px auto;
padding-right: 35px;
transition: border-color 0.3s, box-shadow 0.3s;
width: 100%;
max-width: 300px;
margin: 10px 0;
}
.searchDropdown:hover, .searchDropdown:focus {
border-color: $primary-color-dark;
box-shadow: 0 0 0 3px rgba($primary-color, 0.25);
outline: none;
}
.searchButton {
padding: 10px 15px;
margin: 10px 5px;
padding: 5px;
margin: 10px;
font-size: 22px;
font-family: "Biwa";
background-color: $primary-color;
color: $white;
border: none;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s, transform 0.1s;
min-width: 120px;
}
.searchButton:hover {
background-color: $primary-color-dark;
}
.searchButton:active {
transform: translateY(1px);
}
.searchResult {
border-bottom: 1px solid $primary-color;
padding: 10px 5px;
border-bottom: 1px solid #b2a336;
padding: 5px;
}
.searchResultHeader {
@ -110,172 +53,18 @@ input {
}
.additionalNotes {
color: $accent-color;
color: #a63333;
}
.semField {
font-variant: small-caps;
color: $accent-color-dark;
color: #6a3131;
}
.saimiarNounMorpho {
color: $accent-color;
color: #a63333;
i {
color: $accent-color-dark;
}
}
.passwordDialog {
padding: 20px;
border-radius: 4px;
border: 1px solid $primary-color;
background-color: $background-color;
width: 90%;
max-width: 400px;
}
.passwordDialog h3 {
margin-top: 0;
}
.passwordDialog input {
margin-bottom: 10px;
width: 100%;
}
.dialogButtons {
display: flex;
justify-content: space-between;
}
.passwordDialog button {
margin: 5px;
padding: 8px 15px;
background-color: $primary-color;
color: $white;
border: none;
border-radius: 4px;
cursor: pointer;
min-width: 80px;
}
.passwordDialog button:hover {
background-color: $primary-color-dark;
}
.searchControls {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
margin-top: 1rem;
}
.buttonGroup {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-top: 0.5rem;
}
.dropdownContainer {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
margin-bottom: 10px;
}
.dropdownLabel {
font-weight: bold;
margin-bottom: 5px;
color: $accent-color-dark;
font-size: 18px;
}
/* Responsive styles for mobile devices */
@media (max-width: 768px) {
body {
font-size: 16pt;
}
h1 {
font-size: 1.8rem;
}
div .textInput {
max-width: 100%;
font-size: 18px;
}
.searchDropdown {
font-size: 18px;
padding: 12px 15px;
margin: 10px auto;
width: 100%;
max-width: none;
}
.search {
display: flex;
flex-direction: column;
align-items: center;
}
.search button {
width: 100%;
margin: 5px 0;
}
.searchControls {
width: 100%;
}
.buttonGroup {
flex-direction: column;
width: 100%;
}
.dropdownContainer {
margin-bottom: 5px;
}
.dropdownLabel {
font-size: 16px;
}
.searchButton {
font-size: 18px;
padding: 12px 15px;
margin: 5px 0;
width: 100%;
}
}
@media (max-width: 480px) {
body {
font-size: 14pt;
}
h1 {
font-size: 1.5rem;
}
main {
padding: 0.5rem;
}
div .container {
padding: 0 0.5rem;
}
.searchDropdown {
font-size: 16px;
padding: 10px 12px;
}
.searchButton {
font-size: 16px;
padding: 10px 12px;
color: #6a3131;
}
}

View File

@ -25,17 +25,9 @@ const PasswordDialog = (_props) => {
return (
<dialog className="passwordDialog">
<h3>Enter Password</h3>
<input
type="password"
placeholder="enter password"
value={password}
onChange={ (evt) => setPasswordStr(evt.target.value) }
/>
<div className="dialogButtons">
<button onClick={save}>Save</button>
<button onClick={cancel}>Cancel</button>
</div>
<input type="password" placeholder="enter password" value={password} onChange={ (evt) => setPasswordStr(evt.target.value) } />
<button onClick={save}>Save</button>
<button onClick={cancel}>Cancel</button>
</dialog>);
};
@ -95,7 +87,7 @@ const Results = (props: ResultsProps) => {
const num = props.searchResults.length;
const renderedName = renderConlang(conlang);
const searchType = (props.direction === SearchDirection.ToConlang) ? `English ${renderedName}` : `${renderedName} English`;
const searchType = (props.direction === SearchDirection.ToConlang) ? `English -> ${renderedName}` : `${renderedName} -> English`;
const result = num === 1 ? "result" : "results";
const header = (
<div className="searchResultHeader" key="header">
@ -143,22 +135,15 @@ const App = (_props) => {
};
const handleSearch = (direction: SearchDirection) => {
// First convert any shorthand notation
let processedSearchTerm = direction === SearchDirection.ToEnglish ?
convertSearchBoxShorthand(searchBoxInput, conlang) :
searchBoxInput;
// Then normalize to lowercase
processedSearchTerm = processedSearchTerm.toLowerCase();
if (processedSearchTerm === "") {
const searchTerm = direction === SearchDirection.ToEnglish ? convertSearchBoxShorthand(searchBoxInput, conlang) : searchBoxInput;
if (searchTerm === "") {
setSearchResults(null);
setSearchTerm(null);
setDirection(null);
} else {
searchEntry(processedSearchTerm, conlang, direction, (json) => {
searchEntry(searchTerm, conlang, direction, (json) => {
setSearchResults(json);
setSearchTerm(processedSearchTerm);
setSearchTerm(searchTerm);
setDirection(direction);
});
}
@ -172,22 +157,9 @@ const App = (_props) => {
const conlangs = [Conlang.Saimiar, Conlang.Elesu, Conlang.Tukvaysi, Conlang.Juteyuji];
const langSelectDropdown = (
<div className="dropdownContainer">
<label htmlFor="languageSelect" className="dropdownLabel">Select Language:</label>
<select
id="languageSelect"
className="searchDropdown"
value={conlang}
onChange={handleLangChange}
aria-label="Select language"
>
{conlangs.map((conlang) => (
<option value={conlang} key={conlang}>
{renderConlang(conlang)}
</option>
))}
</select>
</div>
<select className="searchDropdown" value={conlang} onChange={ handleLangChange }>
{conlangs.map((conlang) => <option value={conlang} key={conlang}>{renderConlang(conlang)}</option>)}
</select>
);
const showPasswordBox = () => {
@ -201,32 +173,23 @@ const App = (_props) => {
<PasswordDialog />
<div className="container">
<div className="search">
<h1>Kucinako Wordbook of Arzhanai languages</h1>
<h1>Kucinako - Wordbook of Arzhanai languages</h1>
<p><b>Kucinako</b> (<i>Saimiar</i> "word-book") is a dictionary of words in various languages of the world Arzhanø, and their English
equivalents.</p>
<div className="textInput">
<input
className="textInput"
type="text"
value={ searchBoxInput }
onChange={ (evt) => {
setSearchBoxInput(evt.target.value);
}}
placeholder="Enter search term (case-insensitive)"
/>
</div>
<div className="searchControls">
{ langSelectDropdown }
<div className="buttonGroup">
<button onClick={ () => handleSearch(SearchDirection.ToEnglish) } className="searchButton">{renderConlang(conlang)}</button>
<button onClick={ () => handleSearch(SearchDirection.ToConlang) } className="searchButton">English</button>
<button onClick={ showPasswordBox } className="searchButton">Password</button>
</div>
<input className="textInput" type="text" value={ searchBoxInput } onChange={ (evt) => {
setSearchBoxInput(evt.target.value);
} } />
</div>
<br/>
{ langSelectDropdown }
<button onClick={ () => handleSearch(SearchDirection.ToEnglish) } className="searchButton">{renderConlang(conlang)}</button>
<button onClick={ () => handleSearch(SearchDirection.ToConlang) } className="searchButton">English</button>
<button onClick={ showPasswordBox } className="searchButton">Password</button>
</div>
<Results
searchResults={ searchResults }
searchTerm={ searchTerm }
searchTerm= { searchTerm }
conlang={ conlang }
direction={ direction }
/>

View File

@ -20,7 +20,6 @@ const EntryBase = (props: BaseProps) => {
display: "flex",
justifyContent: "space-between",
flexDirection: "row",
flexWrap: "wrap",
};
const controlStyle = {
@ -28,20 +27,14 @@ const EntryBase = (props: BaseProps) => {
justifyContent: "space-between",
flexDirection: "row",
minWidth: "20%",
marginTop: "10px",
};
const save = () => {
updateEntry(props.conlang, props.id, english);
};
const engTranslation = editing ?
<input
type="text"
value={ english }
onChange={ (evt) => setEnglish(evt.target.value) }
style={{ width: "100%", marginTop: "5px" }}
/> : english;
const engTranslation = editing ? <input type="text" value={ english } onChange={ (evt) => setEnglish(evt.target.value) }/>
: english;
const EditControls = ({onSave}: { onSave: () => void }) => {
const cancel = () => setEditing(false);
@ -88,18 +81,11 @@ const SaiEntry = (props: {entry: SaiEntryProps}) => {
if (isNominal) {
const decl = declineSaimiar(entry);
if (decl) {
morphology = (
<table className="saimiarNounMorpho">
<tr>
<td>Abs: <i>{decl.abs}</i></td><td>Erg: <i>{decl.erg}</i></td><td>Adp: <i>{decl.adp}</i></td>
</tr>
<tr>
<td>All: <i>{decl.all}</i></td><td>Loc: <i>{decl.loc}</i></td><td>Ell: <i>{decl.ell}</i></td>
</tr>
<tr>
<td>Inst: <i>{decl.inst}</i></td><td>Rel: <i>{decl.rel}</i></td><td></td>
</tr>
</table>);
morphology = (<span className="saimiarNounMorpho">
Abs: <i>{decl.abs}</i>, Erg: <i>{decl.erg}</i>, Adp: <i>{decl.adp}</i>,
All: <i>{decl.all}</i>, Loc: <i>{decl.loc}</i>, Ell: <i>{decl.ell}</i>,
Inst: <i>{decl.inst}</i>, Rel: <i>{decl.rel}</i>
</span>);
}
}

View File

@ -1,8 +1,6 @@
{
"compilerOptions": {
"lib": ["ES2020", "dom"],
"jsx": "react",
"esModuleInterop": true
"lib": ["ES2020", "dom"]
},
"include": ["src/**/*"]
}

802
yarn.lock

File diff suppressed because it is too large Load Diff