From 41278b03f9f7b40c0abbd120d40fab90d685743a Mon Sep 17 00:00:00 2001 From: Admin Date: Wed, 7 Oct 2026 19:28:26 +0000 Subject: [PATCH] Mirror Actions folio patch from cf7fd11b3332ef9a59f50bb929bc86c7ad58585f --- src/App.vue | 23 ++++++++++++++++------- src/style.css | 23 +++++++++++++++++++++++ 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/App.vue b/src/App.vue index 19dcebd..74c08c7 100644 --- a/src/App.vue +++ b/src/App.vue @@ -12,6 +12,7 @@ const c=computed(()=>store.all.find(x=>x.id===activeId.value)); const draft=ref(null),hpAmount=ref(1),restType=ref<'short-rest'|'long-rest'>('short-rest'),rollResult=ref(''),selectedAction=ref(null); const drawerIcon=computed(()=>({Character:'sheet',Account:'notes','Hit points':'hp','Add action':'actions','Add resource':'charges','Manage resources':'slots','Add spell':'spells',Roll:'actions',Dice:'initiative',Rest:restType.value==='long-rest'?'long-rest':'short-rest'} as Record)[drawer.value]||'sheet'); const restChanges=computed(()=>c.value?restPreview(c.value,restType.value):[]); +const editingActionId=ref(null); const actionDraft=ref({name:'',bonus:0,damage:'',notes:''}),resourceDraft=ref({name:'',current:1,max:1,reset:'long-rest' as 'short-rest'|'long-rest'|'manual'}),spellDraft=ref({name:'',level:1,prepared:true,notes:''}); watch(c,()=>{if(c.value&&user.value)store.persist(c.value)},{deep:true}); async function authenticate(){try{const response=await fetch('/api/session',{cache:'no-store'});if(!response.ok){localStorage.removeItem('character-identity');navigator.serviceWorker.controller?.postMessage('CLEAR_AUTH');throw new Error('Please sign in with Arrowstack to open your characters.')}const identity=await response.json();if(store.owner&&store.owner!==identity.sub){await store.flush();location.reload();return}user.value=identity;error.value='';localStorage.setItem('character-identity',JSON.stringify(user.value))}catch(e){if(e instanceof TypeError){try{const cached=JSON.parse(localStorage.getItem('character-identity')||'null');if(cached?.sub&&cached.expiresAt>Date.now()){user.value=cached;offline.value=true}else throw new Error()}catch{error.value='Connect to the internet to sign in with Arrowstack.'}}else error.value=(e as Error).message}} @@ -20,7 +21,8 @@ function create(){draft.value=newCharacter();drawer.value='Character'} function edit(){if(c.value){draft.value=structuredClone(JSON.parse(JSON.stringify(c.value)));drawer.value='Character'}} function saveDraft(){try{const valid=characterSchema.parse(draft.value);const index=store.all.findIndex(x=>x.id===valid.id);if(index>=0)store.all[index]=valid;else store.all.push(valid);activeId.value=valid.id;store.persist(valid);drawer.value='';error.value=''}catch{message.value='Check your values: level 1–20, abilities 1–30, HP within maximum.'}} function hp(amount:number){if(c.value)changeHP(c.value,amount)} -function addAction(){if(c.value&&actionDraft.value.name.trim()){c.value.actions.push({...actionDraft.value,id:crypto.randomUUID()});actionDraft.value={name:'',bonus:0,damage:'',notes:''};drawer.value=''}} +function actionEditor(action?:Character['actions'][number]){editingActionId.value=action?.id||null;actionDraft.value=action?{name:action.name,bonus:action.bonus,damage:action.damage,notes:action.notes}:{name:'',bonus:0,damage:'',notes:''};drawer.value='Add action'} +function addAction(){if(!c.value||!actionDraft.value.name.trim())return;const value={...actionDraft.value,name:actionDraft.value.name.trim(),id:editingActionId.value||crypto.randomUUID()};if(editingActionId.value){const index=c.value.actions.findIndex(a=>a.id===editingActionId.value);if(index<0){message.value='This action changed. Close the editor and reload it.';return}c.value.actions[index]=value}else c.value.actions.push(value);editingActionId.value=null;actionDraft.value={name:'',bonus:0,damage:'',notes:''};drawer.value=''} function addResource(){if(c.value&&resourceDraft.value.name.trim()&&resourceDraft.value.max>=0&&resourceDraft.value.current>=0&&resourceDraft.value.current<=resourceDraft.value.max){c.value.resources.push({...resourceDraft.value,id:crypto.randomUUID()});resourceDraft.value={name:'',current:1,max:1,reset:'long-rest'};drawer.value=''}else message.value='Remaining uses must be between zero and maximum.'} function addSpell(){if(c.value&&spellDraft.value.name.trim()){c.value.spells.push({...spellDraft.value,id:crypto.randomUUID()});spellDraft.value={name:'',level:1,prepared:true,notes:''};drawer.value=''}} function rest(type:'short-rest'|'long-rest'){restType.value=type;drawer.value='Rest'} @@ -40,7 +42,7 @@ function openAction(action:Character['actions'][number]){selectedAction.value=ac
✦ CHARACTERBY ARROWSTACK{{offline?'Offline session':store.saved?store.syncStatus:'Saving…'}}
- +
Your character collection

{{message}}

@@ -50,19 +52,26 @@ function openAction(action:Character['actions'][number]){selectedAction.value=ac -

Actions

Add attacks, features and custom actions with your own modifiers.

+
+

Actions

+

Your next move starts here.

Add weapon attacks, features and custom actions with your own modifiers.

+
+

{{action.name}}

{{signed(action.bonus)}} {{action.damage?'to hit':'modifier'}}

{{action.damage}}

+
Notes & options

{{action.notes}}

+
+

Spellbook

Record your own spells. Track spell slots with resources on the Sheet tab.

Gear & inventory

Saved automatically

Equipment, currency, attunement and anything you picked up along the way.

-
✦ ARROWSTACK CHARACTER v0.3 · Made for game night
+
✦ ARROWSTACK CHARACTER v0.3 · Made for game night
-