+ {loading &&
Loading…
}
+ {!loading && user && workerDetails.map(w => {
+ const edit = edits[w.id] ?? { name: w.name, info: w.info ?? '', maintenance_mode: w.maintenance_mode };
+ return (
+
+ );
+ })}
+
+
);
};
diff --git a/src/games/horde/components/modals/options-modal.tsx b/src/games/horde/components/modals/options-modal.tsx
index 1deab33..b5644c7 100644
--- a/src/games/horde/components/modals/options-modal.tsx
+++ b/src/games/horde/components/modals/options-modal.tsx
@@ -1,6 +1,8 @@
-import { useEffect, useState } from "preact/hooks";
-import { X } from "lucide-preact";
+import clsx from "clsx";
+import { useEffect } from "preact/hooks";
import { useHordeState } from "../../contexts/state";
+import { useInputState } from "@common/hooks/useInputState";
+import { Modal } from "@common/components/modal/Modal";
import modalStyles from "../../assets/modal.module.css";
import styles from "../../assets/options-modal.module.css";
@@ -11,21 +13,12 @@ interface Props {
export const OptionsModal = ({ open, onClose }: Props) => {
const { state, dispatch } = useHordeState();
- const [draft, setDraft] = useState(state.apiKey);
+ const [draft, setDraft] = useInputState(state.apiKey);
useEffect(() => {
if (open) setDraft(state.apiKey);
}, [open]);
- useEffect(() => {
- if (!open) return;
- const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };
- window.addEventListener('keydown', handler);
- return () => window.removeEventListener('keydown', handler);
- }, [open]);
-
- if (!open) return null;
-
const save = () => {
dispatch({ type: 'SET_API_KEY', apiKey: draft.trim() });
onClose();
@@ -38,30 +31,27 @@ export const OptionsModal = ({ open, onClose }: Props) => {
};
return (
-
{ if (e.target === e.currentTarget) onClose(); }}>
-
-
-
-
-
-
+
+
-
+
+
+
+
+
);
};
diff --git a/src/games/storywriter/assets/chapters-editor.module.css b/src/games/storywriter/assets/chapters-editor.module.css
index 0bf6403..0d4188e 100644
--- a/src/games/storywriter/assets/chapters-editor.module.css
+++ b/src/games/storywriter/assets/chapters-editor.module.css
@@ -7,18 +7,11 @@
}
.empty {
- color: var(--text-muted);
- font-style: italic;
- font-size: 14px;
+ composes: empty from '@common/assets/ui.module.css';
}
.chapterCard {
- background: var(--bg-secondary);
- border-radius: 8px;
- padding: 20px;
- display: flex;
- flex-direction: column;
- gap: 16px;
+ composes: card from '@common/assets/ui.module.css';
}
.chapterTitle {
@@ -59,24 +52,9 @@
}
.summaryEditable {
- width: 100%;
+ composes: textarea from '@common/assets/ui.module.css';
padding: 10px 12px;
- background: var(--bg);
- border: 1px solid var(--border);
border-radius: 6px;
- font-size: 14px;
- color: var(--text);
- font-family: inherit;
- box-sizing: border-box;
- min-height: 80px;
white-space: pre-wrap;
word-wrap: break-word;
- resize: vertical;
-
- &:focus {
- outline: none;
- border-color: var(--accent);
- }
-
-
}
diff --git a/src/games/storywriter/assets/character-editor.module.css b/src/games/storywriter/assets/character-editor.module.css
index 2e4278d..33f6c52 100644
--- a/src/games/storywriter/assets/character-editor.module.css
+++ b/src/games/storywriter/assets/character-editor.module.css
@@ -7,11 +7,7 @@
}
.header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding-bottom: 16px;
- border-bottom: 1px solid var(--border);
+ composes: editorHeader from '@common/assets/ui.module.css';
}
.header h2 {
@@ -22,19 +18,8 @@
}
.addButton {
- padding: 8px 16px;
- background: var(--accent);
- color: var(--bg);
- border: none;
+ composes: buttonPrimary from '@common/assets/ui.module.css';
border-radius: 6px;
- font-size: 14px;
- font-weight: 500;
- cursor: pointer;
- transition: opacity 0.2s;
-
- &:hover {
- background: var(--accent-alt);
- }
}
.deleteConfirm {
@@ -53,37 +38,12 @@
}
}
-.confirmButton,
-.cancelButton {
- padding: 4px 10px;
- border: 1px solid transparent;
- border-radius: var(--radius);
- font-size: 12px;
- font-weight: 600;
- cursor: pointer;
- transition: all var(--transition);
-}
-
.confirmButton {
- background: var(--accent);
- color: var(--bg);
- border-color: var(--accent);
-
- &:hover {
- background: var(--bg);
- color: var(--accent);
- }
+ composes: confirmButton from '@common/assets/ui.module.css';
}
.cancelButton {
- background: transparent;
- color: var(--text-muted);
- border-color: var(--border);
-
- &:hover {
- background: var(--bg-hover);
- color: var(--text);
- }
+ composes: cancelButton from '@common/assets/ui.module.css';
}
.list {
@@ -95,24 +55,15 @@
}
.empty {
- color: var(--text-muted);
- font-style: italic;
- font-size: 14px;
+ composes: empty from '@common/assets/ui.module.css';
}
.characterCard {
- background: var(--bg-secondary);
- border-radius: 8px;
- padding: 20px;
- display: flex;
- flex-direction: column;
- gap: 16px;
+ composes: card from '@common/assets/ui.module.css';
}
.cardHeader {
- display: flex;
- align-items: center;
- gap: 12px;
+ composes: cardHeader from '@common/assets/ui.module.css';
}
.nameInput {
@@ -133,29 +84,15 @@
}
.deleteButton {
+ composes: deleteButton from '@common/assets/ui.module.css';
width: 32px;
height: 32px;
- display: flex;
- align-items: center;
- justify-content: center;
- background: transparent;
- border: 1px solid var(--border);
border-radius: 6px;
- color: var(--text-muted);
font-size: 20px;
- cursor: pointer;
- transition: all var(--transition);
-
- &:hover {
- background: var(--accent);
- border-color: var(--accent);
- color: var(--bg);
- }
}
.field {
- display: flex;
- flex-direction: column;
+ composes: formGroup from '@common/assets/ui.module.css';
gap: 8px;
}
@@ -224,70 +161,23 @@
}
.generateButton {
- padding: 4px 10px;
- background: var(--bg);
- border: 1px solid var(--border);
- border-radius: 4px;
- font-size: 12px;
- color: var(--text);
- cursor: pointer;
- transition: all var(--transition);
-
- &:hover {
- border-color: var(--accent);
- color: var(--accent);
- }
+ composes: buttonSmall from '@common/assets/ui.module.css';
}
.textarea {
- width: 100%;
+ composes: textarea from '@common/assets/ui.module.css';
padding: 10px 12px;
- background: var(--bg);
- border: 1px solid var(--border);
border-radius: 6px;
- font-size: 14px;
- color: var(--text);
- font-family: inherit;
- resize: vertical;
- box-sizing: border-box;
-
- &:focus {
- outline: none;
- border-color: var(--accent);
- }
}
.select {
- width: 100%;
+ composes: select from '@common/assets/ui.module.css';
padding: 10px 12px;
- background: var(--bg);
- border: 1px solid var(--border);
border-radius: 6px;
- font-size: 14px;
- color: var(--text);
- font-family: inherit;
- cursor: pointer;
-
- &:focus {
- outline: none;
- border-color: var(--accent);
- }
}
.smallButton {
- padding: 4px 10px;
- background: var(--bg);
- border: 1px solid var(--border);
- border-radius: 4px;
- font-size: 12px;
- color: var(--text);
- cursor: pointer;
- transition: all 0.2s;
-
- &:hover {
- border-color: var(--accent);
- color: var(--accent);
- }
+ composes: buttonSmall from '@common/assets/ui.module.css';
}
.nicknames {
@@ -303,35 +193,11 @@
}
.badge {
- display: inline-flex;
- align-items: center;
- gap: 6px;
- padding: 4px 10px;
- background: var(--bg-active);
- border-radius: 16px;
- font-size: 13px;
- color: var(--text);
+ composes: badge from '@common/assets/ui.module.css';
}
.badgeRemove {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 16px;
- height: 16px;
- padding: 0;
- background: transparent;
- border: none;
- border-radius: 50%;
- color: var(--text-muted);
- cursor: pointer;
- font-size: 14px;
- line-height: 1;
-
- &:hover {
- background: var(--danger);
- color: var(--bg);
- }
+ composes: badgeRemove from '@common/assets/ui.module.css';
}
.relations {
diff --git a/src/games/storywriter/assets/location-editor.module.css b/src/games/storywriter/assets/location-editor.module.css
index 2f8b1d5..5b90069 100644
--- a/src/games/storywriter/assets/location-editor.module.css
+++ b/src/games/storywriter/assets/location-editor.module.css
@@ -7,11 +7,7 @@
}
.header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding-bottom: 16px;
- border-bottom: 1px solid var(--border);
+ composes: editorHeader from '@common/assets/ui.module.css';
}
.header h2 {
@@ -22,19 +18,8 @@
}
.addButton {
- padding: 8px 16px;
- background: var(--accent);
- color: var(--bg);
- border: none;
+ composes: buttonPrimary from '@common/assets/ui.module.css';
border-radius: 6px;
- font-size: 14px;
- font-weight: 500;
- cursor: pointer;
- transition: opacity 0.2s;
-
- &:hover {
- background: var(--accent-alt);
- }
}
.deleteConfirm {
@@ -53,37 +38,12 @@
}
}
-.confirmButton,
-.cancelButton {
- padding: 4px 10px;
- border: 1px solid transparent;
- border-radius: var(--radius);
- font-size: 12px;
- font-weight: 600;
- cursor: pointer;
- transition: all var(--transition);
-}
-
.confirmButton {
- background: var(--accent);
- color: var(--bg);
- border-color: var(--accent);
-
- &:hover {
- background: var(--bg);
- color: var(--accent);
- }
+ composes: confirmButton from '@common/assets/ui.module.css';
}
.cancelButton {
- background: transparent;
- color: var(--text-muted);
- border-color: var(--border);
-
- &:hover {
- background: var(--bg-hover);
- color: var(--text);
- }
+ composes: cancelButton from '@common/assets/ui.module.css';
}
.list {
@@ -95,24 +55,15 @@
}
.empty {
- color: var(--text-muted);
- font-style: italic;
- font-size: 14px;
+ composes: empty from '@common/assets/ui.module.css';
}
.locationCard {
- background: var(--bg-secondary);
- border-radius: 8px;
- padding: 20px;
- display: flex;
- flex-direction: column;
- gap: 16px;
+ composes: card from '@common/assets/ui.module.css';
}
.cardHeader {
- display: flex;
- align-items: center;
- gap: 12px;
+ composes: cardHeader from '@common/assets/ui.module.css';
}
.nameInput {
@@ -133,29 +84,15 @@
}
.deleteButton {
+ composes: deleteButton from '@common/assets/ui.module.css';
width: 32px;
height: 32px;
- display: flex;
- align-items: center;
- justify-content: center;
- background: transparent;
- border: 1px solid var(--border);
border-radius: 6px;
- color: var(--text-muted);
font-size: 20px;
- cursor: pointer;
- transition: all var(--transition);
-
- &:hover {
- background: var(--accent);
- border-color: var(--accent);
- color: var(--bg);
- }
}
.field {
- display: flex;
- flex-direction: column;
+ composes: formGroup from '@common/assets/ui.module.css';
gap: 8px;
}
@@ -174,52 +111,17 @@
}
.select {
- width: 100%;
+ composes: select from '@common/assets/ui.module.css';
padding: 10px 12px;
- background: var(--bg);
- border: 1px solid var(--border);
border-radius: 6px;
- font-size: 14px;
- color: var(--text);
- font-family: inherit;
- cursor: pointer;
-
- &:focus {
- outline: none;
- border-color: var(--accent);
- }
}
.generateButton {
- padding: 4px 10px;
- background: var(--bg);
- border: 1px solid var(--border);
- border-radius: 4px;
- font-size: 12px;
- color: var(--text);
- cursor: pointer;
- transition: all var(--transition);
-
- &:hover {
- border-color: var(--accent);
- color: var(--accent);
- }
+ composes: buttonSmall from '@common/assets/ui.module.css';
}
.textarea {
- width: 100%;
+ composes: textarea from '@common/assets/ui.module.css';
padding: 10px 12px;
- background: var(--bg);
- border: 1px solid var(--border);
border-radius: 6px;
- font-size: 14px;
- color: var(--text);
- font-family: inherit;
- resize: vertical;
- box-sizing: border-box;
-
- &:focus {
- outline: none;
- border-color: var(--accent);
- }
}
diff --git a/src/games/storywriter/assets/lore-editor.module.css b/src/games/storywriter/assets/lore-editor.module.css
index 4e2c820..3197af6 100644
--- a/src/games/storywriter/assets/lore-editor.module.css
+++ b/src/games/storywriter/assets/lore-editor.module.css
@@ -7,11 +7,7 @@
}
.header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding-bottom: 16px;
- border-bottom: 1px solid var(--border);
+ composes: editorHeader from '@common/assets/ui.module.css';
}
.header h2 {
@@ -44,19 +40,8 @@
}
.addButton {
- padding: 8px 16px;
- background: var(--accent);
- color: var(--bg);
- border: none;
+ composes: buttonPrimary from '@common/assets/ui.module.css';
border-radius: 6px;
- font-size: 14px;
- font-weight: 500;
- cursor: pointer;
- transition: opacity 0.2s;
-
- &:hover {
- background: var(--accent-alt);
- }
}
.list {
@@ -68,25 +53,17 @@
}
.empty {
- color: var(--text-muted);
- font-style: italic;
- font-size: 14px;
+ composes: empty from '@common/assets/ui.module.css';
}
.entryCard {
- background: var(--bg-secondary);
- border-radius: 8px;
- padding: 20px;
- display: flex;
- flex-direction: column;
+ composes: card from '@common/assets/ui.module.css';
gap: 12px;
}
.cardHeader {
- display: flex;
- align-items: center;
+ composes: cardHeader from '@common/assets/ui.module.css';
justify-content: space-between;
- gap: 12px;
}
.titleRow {
@@ -131,49 +108,15 @@
}
.moveButton {
- width: 28px;
- height: 28px;
- display: flex;
- align-items: center;
- justify-content: center;
+ composes: iconButton from '@common/assets/ui.module.css';
background: var(--bg);
- border: 1px solid var(--border);
- border-radius: 4px;
color: var(--text);
font-size: 14px;
- cursor: pointer;
- transition: all var(--transition);
-
- &:hover:not(:disabled) {
- border-color: var(--accent);
- color: var(--accent);
- }
-
- &:disabled {
- opacity: 0.3;
- cursor: not-allowed;
- }
}
.deleteButton {
- width: 28px;
- height: 28px;
- display: flex;
- align-items: center;
- justify-content: center;
- background: transparent;
- border: 1px solid var(--border);
- border-radius: 4px;
- color: var(--text-muted);
+ composes: deleteButton from '@common/assets/ui.module.css';
font-size: 18px;
- cursor: pointer;
- transition: all var(--transition);
-
- &:hover {
- background: var(--danger);
- border-color: var(--danger);
- color: var(--bg);
- }
}
.content {
@@ -181,20 +124,7 @@
}
.textarea {
- width: 100%;
+ composes: textarea from '@common/assets/ui.module.css';
padding: 10px 12px;
- background: var(--bg);
- border: 1px solid var(--border);
border-radius: 6px;
- font-size: 14px;
- color: var(--text);
- font-family: inherit;
- resize: vertical;
- min-height: 80px;
- box-sizing: border-box;
-
- &:focus {
- outline: none;
- border-color: var(--accent);
- }
}
diff --git a/src/games/storywriter/assets/settings-modal.module.css b/src/games/storywriter/assets/settings-modal.module.css
index 1958a61..d261703 100644
--- a/src/games/storywriter/assets/settings-modal.module.css
+++ b/src/games/storywriter/assets/settings-modal.module.css
@@ -107,16 +107,11 @@
}
.form {
- display: flex;
- flex-direction: column;
- gap: 16px;
- flex: 1;
+ composes: form from '@common/assets/ui.module.css';
}
.formGroup {
- display: flex;
- flex-direction: column;
- gap: 4px;
+ composes: formGroup from '@common/assets/ui.module.css';
}
.formGroupFill {
@@ -124,9 +119,7 @@
}
.label {
- display: block;
- margin-bottom: 4px;
- font-weight: bold;
+ composes: label from '@common/assets/ui.module.css';
}
.selectMultiline {
@@ -143,23 +136,21 @@
padding-inline-start: 16px;
}
-.input,
-.select,
-.textarea {
- width: 100%;
- padding: 8px;
- border-radius: 4px;
- border: 1px solid var(--border);
- background: var(--bg);
- color: var(--text);
+.input {
+ composes: input from '@common/assets/ui.module.css';
+}
+
+.select {
+ composes: select from '@common/assets/ui.module.css';
}
.textarea {
+ composes: textarea from '@common/assets/ui.module.css';
resize: none;
- font-family: inherit;
font-size: inherit;
line-height: 1.5;
flex: 1;
+ min-height: unset;
}
.footer {
@@ -171,26 +162,20 @@
}
.button {
- padding: 8px 16px;
- border-radius: 4px;
- cursor: pointer;
+ composes: button from '@common/assets/ui.module.css';
}
.buttonSecondary {
- border: 1px solid var(--border);
- background: transparent;
- color: var(--text);
+ composes: buttonSecondary from '@common/assets/ui.module.css';
}
.buttonPrimary {
- border: none;
- background: var(--accent);
+ composes: buttonPrimary from '@common/assets/ui.module.css';
color: var(--accent-text);
}
.inputRow {
- display: flex;
- gap: 8px;
+ composes: inputRow from '@common/assets/ui.module.css';
}
.inputRow .input {
@@ -203,7 +188,7 @@
justify-content: center;
padding: 8px;
border: 1px solid var(--border);
- border-radius: 4px;
+ border-radius: var(--radius);
background: var(--bg);
color: var(--text);
cursor: pointer;
@@ -216,9 +201,7 @@
}
.divider {
- height: 1px;
- background: var(--border);
- margin: 16px 0;
+ composes: divider from '@common/assets/ui.module.css';
}
.tokenList {
@@ -229,27 +212,18 @@
}
.tokenItem {
- display: flex;
- align-items: center;
- gap: 4px;
- padding: 4px 8px;
- background: var(--bg-active);
+ composes: badge from '@common/assets/ui.module.css';
border: 1px solid var(--border);
- border-radius: 4px;
- color: var(--text);
+ border-radius: var(--radius);
}
.tokenRemoveButton {
- background: none;
- border: none;
- cursor: pointer;
+ composes: badgeRemove from '@common/assets/ui.module.css';
padding: 0 2px;
- font-size: 14px;
- color: var(--text-muted);
}
.emptyText {
- color: var(--text-muted);
+ composes: empty from '@common/assets/ui.module.css';
}
@media (max-width: 600px) {