feat: add AI-assisted ISAC authoring
This commit is contained in:
@@ -90,7 +90,8 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
|
||||
SUM(CASE WHEN entity_type IN ('user','user_access','user_moderation','identity') THEN 1 ELSE 0 END) AS users,
|
||||
SUM(CASE WHEN entity_type IN ('role','role_management') THEN 1 ELSE 0 END) AS access,
|
||||
SUM(CASE WHEN entity_type='system_configuration' THEN 1 ELSE 0 END) AS system_configuration,
|
||||
SUM(CASE WHEN entity_type='calculation_configuration' THEN 1 ELSE 0 END) AS calculation_configuration
|
||||
SUM(CASE WHEN entity_type='calculation_configuration' THEN 1 ELSE 0 END) AS calculation_configuration,
|
||||
SUM(CASE WHEN entity_type='ai_assistance' THEN 1 ELSE 0 END) AS ai_assistance
|
||||
FROM audit_log`)).get()
|
||||
]);
|
||||
const pagination = getPagination(url, Number(total?.total ?? 0));
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
{value:'',label:'ALL EVENTS',count:data.summary.total,description:'Complete audit history'},
|
||||
{value:'game_data',label:'GAME DATA',count:data.summary.game_data,description:'Builder-source mutations'},
|
||||
{value:'calculation_configuration',label:'CALCULATION RULES',count:data.summary.calculation_configuration,description:'Combat model revisions and calibration'},
|
||||
{value:'ai_assistance',label:'AI ASSISTANCE',count:data.summary.ai_assistance,description:'Permission-gated mechanic drafting'},
|
||||
{value:'data_moderation',label:'DATA MODERATION',count:data.summary.data_moderation,description:'Proposal lifecycle decisions'},
|
||||
{value:'user',label:'USER OPERATIONS',count:data.summary.users,description:'Access, moderation, identities'},
|
||||
{value:'access_policy',label:'ACCESS POLICY',count:data.summary.access,description:'Role definition changes'}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import SegmentedChoice from '$lib/components/SegmentedChoice.svelte';
|
||||
import MechanicLanguageHelp from '$lib/components/MechanicLanguageHelp.svelte';
|
||||
import PseudoScript from '$lib/components/PseudoScript.svelte';
|
||||
import CalculationAuthoringAssistant from '$lib/components/isac/CalculationAuthoringAssistant.svelte';
|
||||
import { createDraft } from '$lib/builder/loadout';
|
||||
import { formatWeaponCalculationScript } from '$lib/calculations/calculation-script';
|
||||
|
||||
@@ -22,10 +23,10 @@
|
||||
statAttributeId: string;
|
||||
stageKey: string;
|
||||
weight: number | string;
|
||||
softCap: number | string;
|
||||
hardCap: number | string;
|
||||
softCap: number | string | null;
|
||||
hardCap: number | string | null;
|
||||
curveType: string;
|
||||
curveParameter: number | string;
|
||||
curveParameter: number | string | null;
|
||||
weaponCategory: string;
|
||||
};
|
||||
type SkillProfileEditor = {
|
||||
@@ -177,6 +178,14 @@
|
||||
[stages[index], stages[target]] = [stages[target], stages[index]];
|
||||
revisionEditing.stages = stages.map((stage, stageIndex) => ({ ...stage, displayOrder: stageIndex + 1 }));
|
||||
}
|
||||
function applyAiRevision(draft: Omit<RevisionEditor, 'id' | 'basedOnRevisionId'>) {
|
||||
if (!revisionEditing) return;
|
||||
revisionEditing = {
|
||||
...draft,
|
||||
id: revisionEditing.id,
|
||||
basedOnRevisionId: revisionEditing.basedOnRevisionId
|
||||
};
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
const token = String(form?.resultToken ?? '');
|
||||
@@ -243,6 +252,10 @@
|
||||
<input type="hidden" name="stagesJson" value={JSON.stringify(revisionEditing.stages)}/>
|
||||
<input type="hidden" name="profilesJson" value={JSON.stringify(revisionEditing.profiles)}/>
|
||||
<input type="hidden" name="skillProfilesJson" value={JSON.stringify(revisionEditing.skillProfiles)}/>
|
||||
<CalculationAuthoringAssistant
|
||||
currentRevision={revisionEditing}
|
||||
onapply={applyAiRevision}
|
||||
/>
|
||||
<div class="calculation-revision-identity"><label class="field isac-record-name">REVISION NAME<input name="name" bind:value={revisionEditing.name} maxlength="120" required/></label><label class="field">NOTES<textarea name="notes" bind:value={revisionEditing.notes} maxlength="4000"></textarea></label></div>
|
||||
<div class="selection-head"><div><span>ORDERED CALCULATION STAGES</span><small>BASE AND FIRING-CYCLE POSITIONS ARE LOCKED</small></div><b>{revisionEditing.stages.length} STAGES</b></div>
|
||||
<div class="calculation-stage-list">{#each [...revisionEditing.stages].sort((left, right) => left.displayOrder - right.displayOrder) as stage, index}
|
||||
|
||||
@@ -174,6 +174,11 @@
|
||||
attributes={data.stats}
|
||||
valueColumns={talentValueColumns}
|
||||
idPrefix={`protocol-talent-${editing.talent.id || 'new'}`}
|
||||
aiContext={{
|
||||
sourceKind: 'os_protocol_talent',
|
||||
sourceName: editing?.talent?.name ?? '',
|
||||
sourceDescription: editing?.talent?.description_template ?? ''
|
||||
}}
|
||||
allowProtocolUpgradeScaling
|
||||
bind:rule={editing.talent.conditionalRule}
|
||||
/>
|
||||
|
||||
@@ -31,7 +31,7 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
|
||||
}, 'name');
|
||||
const permissionPrefix = filter === 'isac' ? 'isac.%' : filter === 'data' ? 'data.%' : filter === 'users' ? 'users.%' : filter === 'audit' ? 'audit.%' : filter === 'system' ? 'system.%' : '';
|
||||
const relatedPermissionFilter = filter === 'data'
|
||||
? " OR p.name LIKE 'calculation.%'"
|
||||
? " OR p.name LIKE 'calculation.%' OR p.name LIKE 'ai.%'"
|
||||
: filter === 'system'
|
||||
? " OR p.name LIKE 'site.%' OR p.name LIKE 'metrics.%' OR p.name LIKE 'images.%'"
|
||||
: '';
|
||||
@@ -47,7 +47,7 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
|
||||
const [summary, filtered, permissions] = await Promise.all([
|
||||
(await database.prepare(`SELECT COUNT(*) AS total,
|
||||
SUM(CASE WHEN EXISTS (SELECT 1 FROM role_permissions rp JOIN permissions p ON p.id=rp.permission_id WHERE rp.role_id=roles.id AND p.name LIKE 'isac.%') THEN 1 ELSE 0 END) AS isac,
|
||||
SUM(CASE WHEN EXISTS (SELECT 1 FROM role_permissions rp JOIN permissions p ON p.id=rp.permission_id WHERE rp.role_id=roles.id AND (p.name LIKE 'data.%' OR p.name LIKE 'calculation.%')) THEN 1 ELSE 0 END) AS data,
|
||||
SUM(CASE WHEN EXISTS (SELECT 1 FROM role_permissions rp JOIN permissions p ON p.id=rp.permission_id WHERE rp.role_id=roles.id AND (p.name LIKE 'data.%' OR p.name LIKE 'calculation.%' OR p.name LIKE 'ai.%')) THEN 1 ELSE 0 END) AS data,
|
||||
SUM(CASE WHEN EXISTS (SELECT 1 FROM role_permissions rp JOIN permissions p ON p.id=rp.permission_id WHERE rp.role_id=roles.id AND p.name LIKE 'users.%') THEN 1 ELSE 0 END) AS users,
|
||||
SUM(CASE WHEN EXISTS (SELECT 1 FROM role_permissions rp JOIN permissions p ON p.id=rp.permission_id WHERE rp.role_id=roles.id AND p.name LIKE 'audit.%') THEN 1 ELSE 0 END) AS audit
|
||||
,SUM(CASE WHEN EXISTS (SELECT 1 FROM role_permissions rp JOIN permissions p ON p.id=rp.permission_id WHERE rp.role_id=roles.id AND (p.name LIKE 'system.%' OR p.name LIKE 'site.%' OR p.name LIKE 'metrics.%' OR p.name LIKE 'images.%')) THEN 1 ELSE 0 END) AS system
|
||||
|
||||
@@ -121,6 +121,11 @@
|
||||
allowAssignedAbility
|
||||
valueColumns={talentValueColumns}
|
||||
idPrefix={`skill-chip-talent-${editing.talent.id || 'new'}`}
|
||||
aiContext={{
|
||||
sourceKind: 'skill_chip_talent',
|
||||
sourceName: editing?.talent?.name ?? '',
|
||||
sourceDescription: editing?.talent?.description_template ?? ''
|
||||
}}
|
||||
bind:rule={editing.talent.conditionalRule}
|
||||
/>
|
||||
</section>
|
||||
|
||||
@@ -574,6 +574,11 @@
|
||||
attributes={data.attributes}
|
||||
valueColumns={conditionalValueColumns}
|
||||
idPrefix={`specialization-rank-${rank.rank}-conditional-${combatIndex}`}
|
||||
aiContext={{
|
||||
sourceKind: 'specialization_tree_rank',
|
||||
sourceName: combatEffectById(combatEffect.effectId)?.name ?? `${nodeEditing?.name ?? 'Tree node'} · Rank ${rank.rank}`,
|
||||
sourceDescription: rank?.description ?? ''
|
||||
}}
|
||||
bind:rule={combatEffect.conditionalRule}
|
||||
/>
|
||||
</section>
|
||||
|
||||
@@ -102,6 +102,11 @@
|
||||
attributes={data.stats}
|
||||
valueColumns={[{key:'tierI',variantKey:'I',label:'TIER I'},{key:'tierII',variantKey:'II',label:'TIER II'}]}
|
||||
idPrefix={`talent-${talent.id || 'new'}`}
|
||||
aiContext={{
|
||||
sourceKind: 'talent',
|
||||
sourceName: talent?.name ?? '',
|
||||
sourceDescription: talent?.descriptionTemplate ?? ''
|
||||
}}
|
||||
bind:rule={talent.conditionalRule}
|
||||
/>
|
||||
{#if form?.talentError}<p class="error">{form.talentError}</p>{/if}
|
||||
|
||||
@@ -123,6 +123,11 @@
|
||||
attributes={data.stats}
|
||||
valueColumns={[{key:'tierI',variantKey:'I',label:'TIER I'},{key:'tierII',variantKey:'II',label:'TIER II'}]}
|
||||
idPrefix={`weapon-exotic-${editing.exoticTalent.id || 'new'}`}
|
||||
aiContext={{
|
||||
sourceKind: 'exotic_weapon_talent',
|
||||
sourceName: editing?.exoticTalent?.name ?? '',
|
||||
sourceDescription: editing?.exoticTalent?.descriptionTemplate ?? ''
|
||||
}}
|
||||
bind:rule={editing.exoticTalent.conditionalRule}
|
||||
/>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user