diff --git a/index.html b/index.html
index 1a8f579..4672da1 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
-
+
diff --git a/package.json b/package.json
index 32702c2..4735835 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "amerc-site",
- "version": "0.34.0-share",
+ "version": "0.35.0-account",
"private": true,
"type": "module",
"scripts": {
diff --git a/src/Account.jsx b/src/Account.jsx
new file mode 100644
index 0000000..84fecff
--- /dev/null
+++ b/src/Account.jsx
@@ -0,0 +1,48 @@
+import React, { useState } from 'react';
+import { api } from './api.js';
+import { KeysCard } from './AgentPlatform.jsx';
+import { toast } from './toast.js';
+
+function avatarStyle(name) { let h = 0; for (const ch of String(name)) h = (h * 31 + ch.charCodeAt(0)) >>> 0; const a = h % 360, b = (a + 70 + h % 90) % 360; return { background: `linear-gradient(135deg, hsl(${a} 68% 46%), hsl(${b} 64% 32%))` }; }
+
+export default function Account({ auth, setRoute }) {
+ const [cur, setCur] = useState('');
+ const [nw, setNw] = useState('');
+ const [err, setErr] = useState('');
+ if (!auth.ready) return
;
+ if (!auth.user) return Account
Log in to manage your account.
;
+ const u = auth.user;
+ const changePw = async (e) => {
+ e.preventDefault(); setErr('');
+ try { await api('/auth/password', { method: 'POST', body: { currentPassword: cur, newPassword: nw } }); setCur(''); setNw(''); toast('Password updated ✓'); }
+ catch (e2) { setErr(e2.message); }
+ };
+ return (
+
+
Account
+
+
+
{u.handle.slice(0, 2).toUpperCase()}
+
{u.handle}
+
{u.email}
+
{u.role}
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/AgentPlatform.jsx b/src/AgentPlatform.jsx
index 82aa77c..dd1c6a7 100644
--- a/src/AgentPlatform.jsx
+++ b/src/AgentPlatform.jsx
@@ -165,7 +165,7 @@ export function UsePanel({ instance, onClose }) {
}
// ---- My Booth: publish classes + instances, manage ------------------------
-function KeysCard() {
+export function KeysCard() {
const [keys, setKeys] = useState([]);
const [name, setName] = useState('');
const [created, setCreated] = useState(null);
diff --git a/src/Scene2D.jsx b/src/Scene2D.jsx
index 05a9946..29c5db8 100644
--- a/src/Scene2D.jsx
+++ b/src/Scene2D.jsx
@@ -3,10 +3,11 @@ import { Menu, X } from 'lucide-react';
import { useAuth, AuthPanel, AdminConsole } from './Auth.jsx';
import { AgentBrowse, BoothDashboard } from './AgentPlatform.jsx';
import Mansion from './Mansion.jsx';
+import Account from './Account.jsx';
import TavernGame from './TavernGame.jsx';
import { api } from './api.js';
-const RELEASE = '0.34.0-share';
+const RELEASE = '0.35.0-account';
const NAV = [
{ id: 'home', label: 'Tavern' },
@@ -14,7 +15,7 @@ const NAV = [
{ id: 'mansion', label: 'Mansion' },
{ id: 'booth', label: 'My Booth' },
];
-const VALID_ROUTES = ['home', 'agents', 'mansion', 'booth', 'admin', 'signin'];
+const VALID_ROUTES = ['home', 'agents', 'mansion', 'booth', 'account', 'admin', 'signin'];
function useHashRoute() {
const normalize = () => {
@@ -50,7 +51,10 @@ function PixelTopbar({ route, setRoute, auth }) {
{isAdmin && PM}
{isAdmin && }
{loggedIn
- ?
+ ? <>
+
+
+ >
: }
>
);
@@ -192,6 +196,7 @@ function Scene({ route, setRoute, auth }) {
if (route === 'agents') return ;
if (route === 'mansion') return ;
if (route === 'booth') return ;
+ if (route === 'account') return ;
if (route === 'signin') return
setRoute('booth')} />
diff --git a/src/styles.css b/src/styles.css
index 3486b83..0e8f241 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -1159,3 +1159,18 @@ button {
.ap-inst-status.pulse { box-shadow: 0 0 0 0 rgba(54,240,176,0.7); animation: ap-pulse 1.8s infinite; }
.ap-mine-class { display: flex; flex-direction: column; gap: 6px; }
.ap-mine-row .ap-avatar-sm { flex: none; }
+
+/* account / settings (0.35) */
+.acct-grid { display: grid; grid-template-columns: 280px 1fr; gap: 16px; align-items: start; margin-bottom: 6px; }
+.acct-card { padding: 20px; border-radius: 14px; background: #130d20; border: 1px solid #271c3a; }
+.acct-profile { text-align: center; }
+.acct-avatar { width: 64px; height: 64px; border-radius: 16px; font-size: 22px; font-weight: 700; color: #fff; margin: 0 auto 12px; display: flex; align-items: center; justify-content: center; font-family: Georgia, serif; }
+.acct-profile h3 { margin: 0; color: #fff; font-size: 18px; }
+.acct-email { color: #9a86b8; font-size: 13px; margin: 4px 0 10px; word-break: break-all; }
+.acct-role { display: inline-block; font-size: 11px; padding: 3px 11px; border-radius: 12px; text-transform: uppercase; letter-spacing: 1px; }
+.acct-role-admin { background: rgba(242,184,95,0.15); color: #f2b85f; border: 1px solid rgba(242,184,95,0.4); }
+.acct-role-member { background: rgba(70,200,224,0.12); color: #46c8e0; border: 1px solid rgba(70,200,224,0.3); }
+.acct-links { display: flex; flex-direction: column; gap: 8px; margin-top: 18px; }
+.acct-form { display: flex; flex-direction: column; gap: 10px; max-width: 360px; }
+.acct-form input { padding: 10px; border-radius: 8px; background: #0a0f1a; border: 1px solid #2e2440; color: #eaf2ff; font-family: inherit; }
+@media (max-width: 680px) { .acct-grid { grid-template-columns: 1fr; } }