feat(tokens): app-local non-player tokens with session moves and UI polish

Add token library/placements, keep play-time moves for the session, lock presentation interactions, and fix export/import modal layout plus freeform trap label.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-07-27 11:04:12 +08:00
parent bdeb64e356
commit f270812219
44 changed files with 2617 additions and 341 deletions
+10 -14
View File
@@ -15,8 +15,7 @@ import type {
import editorStyles from '../editor/EditorApp.module.css';
import { useEditorI18n } from '../editor/i18n/EditorI18nContext';
import { getDndApi } from '../shared/dndApi';
import { Button, Input } from '../shared/ui/controls';
import controlStyles from '../shared/ui/Controls.module.css';
import { Button, Input, Select } from '../shared/ui/controls';
import { useAssetUrl } from '../shared/useAssetImageUrl';
import { NpcBindingFields } from './NpcBindingFields';
@@ -785,24 +784,21 @@ export function NpcsEditorApp() {
<div>
<div className={styles.fieldLabel}>{t('npcs.group')}</div>
<select
className={controlStyles.input}
<Select
value={selected.groupId ?? ''}
onChange={(e) => {
const groupId = (e.target.value || null) as NpcGroupId | null;
ariaLabel={t('npcs.group')}
onChange={(next) => {
const groupId = (next || null) as NpcGroupId | null;
void api.invoke(ipcChannels.project.updateNpcFields, {
npcId: selected.id,
groupId,
});
}}
>
<option value="">{t('npcs.ungrouped')}</option>
{groupOptions.map((g) => (
<option key={g.id} value={g.id}>
{g.label}
</option>
))}
</select>
options={[
{ value: '', label: t('npcs.ungrouped') },
...groupOptions.map((g) => ({ value: g.id, label: g.label })),
]}
/>
</div>
<div>