@resolveio/client-lib-aicoder-dashboard 21.0.10 → 21.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import*as i0 from"@angular/core";import{Injectable,EventEmitter,Output,Input,Component,NgModule}from"@angular/core";import*as i1 from"@angular/common/http";import{HttpHeaders,HttpClientModule}from"@angular/common/http";import*as i2 from"@resolveio/client-lib-core";import{Observable,throwError,interval}from"rxjs";import{switchMap,map,tap,shareReplay}from"rxjs/operators";import*as i2$1 from"@angular/forms";import{FormsModule}from"@angular/forms";import{CommonModule}from"@angular/common";class AICoderDashboardService{buildDefaultSummary(){return{overview:{app_name:"ResolveIO App",app_status:"Active",plan_tier:"small",max_users:10},database:{name:"resolveio",host:"127.0.0.1",status:"Ready"},infrastructure:{current_tier:"small",available_tiers:["small","medium","large"],max_users:10,backend_status:"Ready"},code_changes:{release_notes:"Latest updates are live and available.",last_deployed_at:"",recent_updates:["Overview tab highlights app status and plan tier.","Infrastructure tab shows Small, Medium, and Large options.","Code Changes tab summarizes customer-facing updates."]},subscription:{current_package:"Small",current_tier:"small",package_price_label:"$99 / month",billing_status:"Paid",billing_mode:"Subscription",subscription_status:"active",current_period_end:"",manage_url:"",can_downgrade:!0,downgrade_reason:""}}}buildTierOptions(){return[{id:"small",label:"Small",description:"Great for focused teams and lighter usage."},{id:"medium",label:"Medium",description:"Balanced capacity for growing teams and workflows."},{id:"large",label:"Large",description:"Higher capacity for heavy usage and larger teams."}]}static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardService,deps:[],target:i0.ɵɵFactoryTarget.Injectable});static"ɵprov"=i0.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardService,providedIn:"root"})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardService,decorators:[{type:Injectable,args:[{providedIn:"root"}]}]});class AICoderDashboardComponent{dashboardService;summary;loading=!1;errorMessage="";activeTab="overview";tierOptions;tabChange=new EventEmitter;retry=new EventEmitter;tierChange=new EventEmitter;manageSubscription=new EventEmitter;constructor(t){this.dashboardService=t,this.summary=this.dashboardService.buildDefaultSummary(),this.tierOptions=this.dashboardService.buildTierOptions()}setTab(t){this.activeTab!==t&&this.tabChange.emit(t)}requestRetry(){this.retry.emit()}requestTierChange(t){this.tierChange.emit(t)}requestManageSubscription(){this.manageSubscription.emit(this.resolveManageSubscriptionUrl())}isCurrentTier(t){return String(this.summary?.infrastructure?.current_tier||"").trim().toLowerCase()===t}hasManageSubscriptionUrl(){return!!this.resolveManageSubscriptionUrl()}resolveManageSubscriptionUrl(){return String(this.summary?.subscription?.manage_url||"").trim()}static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardComponent,deps:[{token:AICoderDashboardService}],target:i0.ɵɵFactoryTarget.Component});static"ɵcmp"=i0.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"21.1.2",type:AICoderDashboardComponent,isStandalone:!1,selector:"resolveio-client-lib-aicoder-dashboard",inputs:{summary:"summary",loading:"loading",errorMessage:"errorMessage",activeTab:"activeTab",tierOptions:"tierOptions"},outputs:{tabChange:"tabChange",retry:"retry",tierChange:"tierChange",manageSubscription:"manageSubscription"},ngImport:i0,template:'<div class="aicoder-dashboard">\n\t<div class="aicoder-dashboard__tabs">\n\t\t<button type="button" [class.is-active]="activeTab === \'overview\'" (click)="setTab(\'overview\')">Overview</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'database\'" (click)="setTab(\'database\')">Database</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'infrastructure\'" (click)="setTab(\'infrastructure\')">Infrastructure</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'code_changes\'" (click)="setTab(\'code_changes\')">Code Changes</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'subscription\'" (click)="setTab(\'subscription\')">Subscription</button>\n\t</div>\n\n\t@if (loading) {\n\t\t<div class="aicoder-dashboard__state">Loading dashboard details...</div>\n\t} @else if (errorMessage) {\n\t\t<div class="aicoder-dashboard__state aicoder-dashboard__state--error">\n\t\t\t<span>{{ errorMessage }}</span>\n\t\t\t<button type="button" (click)="requestRetry()">Retry</button>\n\t\t</div>\n\t} @else {\n\t\t@if (activeTab === \'overview\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>App</span><strong>{{ summary.overview.app_name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.overview.app_status }}</strong></div>\n\t\t\t\t<div class="tile"><span>Tier</span><strong>{{ summary.overview.plan_tier }}</strong></div>\n\t\t\t\t<div class="tile"><span>Max users</span><strong>{{ summary.overview.max_users }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'database\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>Name</span><strong>{{ summary.database.name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Host</span><strong>{{ summary.database.host }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.database.status }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'infrastructure\') {\n\t\t\t<div class="aicoder-dashboard__tiers">\n\t\t\t\t@for (tier of tierOptions; track tier.id) {\n\t\t\t\t\t<div class="tier" [class.tier--active]="isCurrentTier(tier.id)">\n\t\t\t\t\t\t<h4>{{ tier.label }}</h4>\n\t\t\t\t\t\t<p>{{ tier.description }}</p>\n\t\t\t\t\t\t<button type="button" (click)="requestTierChange(tier.id)">Choose {{ tier.label }}</button>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'code_changes\') {\n\t\t\t<div class="aicoder-dashboard__changes">\n\t\t\t\t<p>{{ summary.code_changes.release_notes }}</p>\n\t\t\t\t<ul>\n\t\t\t\t\t@for (item of summary.code_changes.recent_updates; track item) {\n\t\t\t\t\t\t<li>{{ item }}</li>\n\t\t\t\t\t}\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'subscription\') {\n\t\t\t<div class="aicoder-dashboard__subscription">\n\t\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t\t<div class="tile"><span>Package</span><strong>{{ summary.subscription?.current_package || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Tier</span><strong>{{ summary.subscription?.current_tier || summary.overview.plan_tier || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Price</span><strong>{{ summary.subscription?.package_price_label || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Billing status</span><strong>{{ summary.subscription?.billing_status || \'Unknown\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Billing mode</span><strong>{{ summary.subscription?.billing_mode || \'Unknown\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Subscription</span><strong>{{ summary.subscription?.subscription_status || \'N/A\' }}</strong></div>\n\t\t\t\t</div>\n\t\t\t\t@if (summary.subscription?.current_period_end) {\n\t\t\t\t\t<p class="aicoder-dashboard__hint">Current period ends: {{ summary.subscription?.current_period_end }}</p>\n\t\t\t\t}\n\t\t\t\t@if (summary.subscription?.downgrade_reason) {\n\t\t\t\t\t<p class="aicoder-dashboard__hint aicoder-dashboard__hint--warning">{{ summary.subscription?.downgrade_reason }}</p>\n\t\t\t\t}\n\t\t\t\t<div class="aicoder-dashboard__subscription-actions">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype="button"\n\t\t\t\t\t\tclass="aicoder-dashboard__primary-action"\n\t\t\t\t\t\t(click)="requestManageSubscription()"\n\t\t\t\t\t\t[disabled]="!hasManageSubscriptionUrl()"\n\t\t\t\t\t>\n\t\t\t\t\t\tUpgrade Or Manage Plan\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t}\n\t}\n</div>\n',styles:[".aicoder-dashboard{--aicoder-primary: #0f4c81;font-family:inherit}.aicoder-dashboard__tabs{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:.75rem}.aicoder-dashboard__tabs button{border:1px solid rgba(15,23,42,.2);background:#fff;padding:.45rem .75rem;border-radius:8px;font-weight:600}.aicoder-dashboard__tabs button.is-active{border-color:var(--aicoder-primary);background:#0f4c811a}.aicoder-dashboard__state{padding:.85rem;border-radius:10px;background:#f8fafc}.aicoder-dashboard__state--error{display:flex;justify-content:space-between;align-items:center;gap:.5rem;background:#fff7ed}.aicoder-dashboard__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:.75rem}.tile{padding:.8rem;border:1px solid rgba(15,23,42,.12);border-radius:10px;background:#fff}.tile span{display:block;font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:#0f172a9e}.tile strong{display:block;margin-top:.3rem;font-size:1rem}.aicoder-dashboard__tiers{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:.75rem}.tier{padding:.85rem;border-radius:10px;border:1px solid rgba(15,23,42,.12);background:#f8fafc}.tier--active{border-color:var(--aicoder-primary);background:#0f4c8114}.tier h4{margin:0}.tier p{margin:.45rem 0}.aicoder-dashboard__changes ul{margin:0;padding-left:1.1rem}.aicoder-dashboard__subscription{display:grid;gap:.75rem}.aicoder-dashboard__subscription-actions{display:flex;flex-wrap:wrap;gap:.5rem}.aicoder-dashboard__primary-action{border:1px solid var(--aicoder-primary);background:var(--aicoder-primary);color:#fff;padding:.5rem .85rem;border-radius:8px;font-weight:700}.aicoder-dashboard__primary-action[disabled]{opacity:.55;cursor:not-allowed}.aicoder-dashboard__hint{margin:0;color:#334155;font-size:.88rem}.aicoder-dashboard__hint--warning{color:#9a3412}\n"]})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardComponent,decorators:[{type:Component,args:[{selector:"resolveio-client-lib-aicoder-dashboard",standalone:!1,template:'<div class="aicoder-dashboard">\n\t<div class="aicoder-dashboard__tabs">\n\t\t<button type="button" [class.is-active]="activeTab === \'overview\'" (click)="setTab(\'overview\')">Overview</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'database\'" (click)="setTab(\'database\')">Database</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'infrastructure\'" (click)="setTab(\'infrastructure\')">Infrastructure</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'code_changes\'" (click)="setTab(\'code_changes\')">Code Changes</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'subscription\'" (click)="setTab(\'subscription\')">Subscription</button>\n\t</div>\n\n\t@if (loading) {\n\t\t<div class="aicoder-dashboard__state">Loading dashboard details...</div>\n\t} @else if (errorMessage) {\n\t\t<div class="aicoder-dashboard__state aicoder-dashboard__state--error">\n\t\t\t<span>{{ errorMessage }}</span>\n\t\t\t<button type="button" (click)="requestRetry()">Retry</button>\n\t\t</div>\n\t} @else {\n\t\t@if (activeTab === \'overview\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>App</span><strong>{{ summary.overview.app_name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.overview.app_status }}</strong></div>\n\t\t\t\t<div class="tile"><span>Tier</span><strong>{{ summary.overview.plan_tier }}</strong></div>\n\t\t\t\t<div class="tile"><span>Max users</span><strong>{{ summary.overview.max_users }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'database\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>Name</span><strong>{{ summary.database.name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Host</span><strong>{{ summary.database.host }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.database.status }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'infrastructure\') {\n\t\t\t<div class="aicoder-dashboard__tiers">\n\t\t\t\t@for (tier of tierOptions; track tier.id) {\n\t\t\t\t\t<div class="tier" [class.tier--active]="isCurrentTier(tier.id)">\n\t\t\t\t\t\t<h4>{{ tier.label }}</h4>\n\t\t\t\t\t\t<p>{{ tier.description }}</p>\n\t\t\t\t\t\t<button type="button" (click)="requestTierChange(tier.id)">Choose {{ tier.label }}</button>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'code_changes\') {\n\t\t\t<div class="aicoder-dashboard__changes">\n\t\t\t\t<p>{{ summary.code_changes.release_notes }}</p>\n\t\t\t\t<ul>\n\t\t\t\t\t@for (item of summary.code_changes.recent_updates; track item) {\n\t\t\t\t\t\t<li>{{ item }}</li>\n\t\t\t\t\t}\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'subscription\') {\n\t\t\t<div class="aicoder-dashboard__subscription">\n\t\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t\t<div class="tile"><span>Package</span><strong>{{ summary.subscription?.current_package || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Tier</span><strong>{{ summary.subscription?.current_tier || summary.overview.plan_tier || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Price</span><strong>{{ summary.subscription?.package_price_label || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Billing status</span><strong>{{ summary.subscription?.billing_status || \'Unknown\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Billing mode</span><strong>{{ summary.subscription?.billing_mode || \'Unknown\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Subscription</span><strong>{{ summary.subscription?.subscription_status || \'N/A\' }}</strong></div>\n\t\t\t\t</div>\n\t\t\t\t@if (summary.subscription?.current_period_end) {\n\t\t\t\t\t<p class="aicoder-dashboard__hint">Current period ends: {{ summary.subscription?.current_period_end }}</p>\n\t\t\t\t}\n\t\t\t\t@if (summary.subscription?.downgrade_reason) {\n\t\t\t\t\t<p class="aicoder-dashboard__hint aicoder-dashboard__hint--warning">{{ summary.subscription?.downgrade_reason }}</p>\n\t\t\t\t}\n\t\t\t\t<div class="aicoder-dashboard__subscription-actions">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype="button"\n\t\t\t\t\t\tclass="aicoder-dashboard__primary-action"\n\t\t\t\t\t\t(click)="requestManageSubscription()"\n\t\t\t\t\t\t[disabled]="!hasManageSubscriptionUrl()"\n\t\t\t\t\t>\n\t\t\t\t\t\tUpgrade Or Manage Plan\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t}\n\t}\n</div>\n',styles:[".aicoder-dashboard{--aicoder-primary: #0f4c81;font-family:inherit}.aicoder-dashboard__tabs{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:.75rem}.aicoder-dashboard__tabs button{border:1px solid rgba(15,23,42,.2);background:#fff;padding:.45rem .75rem;border-radius:8px;font-weight:600}.aicoder-dashboard__tabs button.is-active{border-color:var(--aicoder-primary);background:#0f4c811a}.aicoder-dashboard__state{padding:.85rem;border-radius:10px;background:#f8fafc}.aicoder-dashboard__state--error{display:flex;justify-content:space-between;align-items:center;gap:.5rem;background:#fff7ed}.aicoder-dashboard__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:.75rem}.tile{padding:.8rem;border:1px solid rgba(15,23,42,.12);border-radius:10px;background:#fff}.tile span{display:block;font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:#0f172a9e}.tile strong{display:block;margin-top:.3rem;font-size:1rem}.aicoder-dashboard__tiers{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:.75rem}.tier{padding:.85rem;border-radius:10px;border:1px solid rgba(15,23,42,.12);background:#f8fafc}.tier--active{border-color:var(--aicoder-primary);background:#0f4c8114}.tier h4{margin:0}.tier p{margin:.45rem 0}.aicoder-dashboard__changes ul{margin:0;padding-left:1.1rem}.aicoder-dashboard__subscription{display:grid;gap:.75rem}.aicoder-dashboard__subscription-actions{display:flex;flex-wrap:wrap;gap:.5rem}.aicoder-dashboard__primary-action{border:1px solid var(--aicoder-primary);background:var(--aicoder-primary);color:#fff;padding:.5rem .85rem;border-radius:8px;font-weight:700}.aicoder-dashboard__primary-action[disabled]{opacity:.55;cursor:not-allowed}.aicoder-dashboard__hint{margin:0;color:#334155;font-size:.88rem}.aicoder-dashboard__hint--warning{color:#9a3412}\n"]}]}],ctorParameters:()=>[{type:AICoderDashboardService}],propDecorators:{summary:[{type:Input}],loading:[{type:Input}],errorMessage:[{type:Input}],activeTab:[{type:Input}],tierOptions:[{type:Input}],tabChange:[{type:Output}],retry:[{type:Output}],tierChange:[{type:Output}],manageSubscription:[{type:Output}]}});class AICoderWorkbenchApiService{http;tokenManager;sessionHeaderName="X-AI-Coder-Session";appTokenHeaderName="X-AI-Coder-App-Token";sessionRefreshSkewMs=6e4;defaultSessionTtlMs=144e5;sessionByApiBase=new Map;exchangeByApiBase=new Map;constructor(t,e){this.http=t,this.tokenManager=e}get(t,e,r=""){return this.authHeaders(t,r).pipe(switchMap(r=>this.http.get(this.buildUrl(t,e),{headers:r})))}post(t,e,r={},i=""){return this.authHeaders(t,i).pipe(switchMap(i=>this.http.post(this.buildUrl(t,e),r,{headers:i})))}patch(t,e,r={},i=""){return this.authHeaders(t,i).pipe(switchMap(i=>this.http.patch(this.buildUrl(t,e),r,{headers:i})))}delete(t,e,r=""){return this.authHeaders(t,r).pipe(switchMap(r=>this.http.delete(this.buildUrl(t,e),{headers:r})))}resolveDefaultApiBase(){const t=String(window?.location?.hostname||"").trim().toLowerCase();return"aicoder.resolveio.com"===t||t.endsWith(".aicoder.resolveio.com")?"https://backend.aicoder.resolveio.com":""}authHeaders(t,e=""){const r=String(e||"").trim();return r?new Observable(t=>{t.next(new HttpHeaders({[this.appTokenHeaderName]:r})),t.complete()}):this.ensureSessionToken(t).pipe(map(t=>new HttpHeaders({[this.sessionHeaderName]:t})))}ensureSessionToken(t){const e=this.normalizeApiBase(t),r=this.sessionByApiBase.get(e);if(r&&r.token&&r.expiresAtMs>Date.now()+this.sessionRefreshSkewMs)return new Observable(t=>{t.next(r.token),t.complete()});const i=this.exchangeByApiBase.get(e);if(i)return i;const a=String(this.tokenManager.getToken("accessToken")||"").trim();if(!a)return throwError(()=>new Error("Sign in before using AICoder."));const s=new HttpHeaders({Authorization:`Bearer ${a}`}),o=this.http.post(this.buildUrl(e,"/api/ai-coder/auth/session"),{},{headers:s}).pipe(map(t=>{const r=String(t?.token||"").trim();if(!r)throw new Error("AICoder session response did not include a token.");return this.sessionByApiBase.set(e,{token:r,expiresAtMs:this.resolveSessionExpiresAtMs(t)}),r}),tap({error:()=>this.sessionByApiBase.delete(e),complete:()=>this.exchangeByApiBase.delete(e)}),shareReplay(1));return this.exchangeByApiBase.set(e,o),o}resolveSessionExpiresAtMs(t){const e=t?.expires_at?new Date(t.expires_at).getTime():0;if(Number.isFinite(e)&&e>Date.now())return e;const r=1e3*Number(t?.expires_in_seconds||0);return Date.now()+(r>0?r:this.defaultSessionTtlMs)}buildUrl(t,e){const r=this.normalizeApiBase(t),i=String(e||"").trim();return r?`${r}${i.startsWith("/")?i:`/${i}`}`:i}normalizeApiBase(t){return String(t||"").trim().replace(/\/+$/,"")}static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchApiService,deps:[{token:i1.HttpClient},{token:i2.TokenManagerService}],target:i0.ɵɵFactoryTarget.Injectable});static"ɵprov"=i0.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchApiService,providedIn:"root"})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchApiService,decorators:[{type:Injectable,args:[{providedIn:"root"}]}],ctorParameters:()=>[{type:i1.HttpClient},{type:i2.TokenManagerService}]});class AICoderWorkbenchComponent{api;appId="";appName="AICoder App";apiBase="";appToken="";defaultTab="chats";compact=!1;showHeader=!0;autoRefresh=!0;activeTab="chats";jobs=[];selectedJob=null;jobLogs=[];deployOperations=[];deployJobs=[];gitStatus=null;isLoading=!1;isSubmitting=!1;errorMessage="";successMessage="";requestTitle="";requestMessage="";chatMessage="";workflowMode="implementation";requestKind="bug_or_feature";dataChangePolicy="review_before_write";deploymentTarget="branch_pr";evidenceNotes="";qaFocus="";riskNotes="";refreshSub;constructor(t){this.api=t}ngOnInit(){this.activeTab=this.normalizeTab(this.defaultTab),this.apiBase||(this.apiBase=this.api.resolveDefaultApiBase()),this.refresh(),this.startAutoRefresh()}ngOnChanges(t){t.defaultTab&&this.defaultTab&&(this.activeTab=this.normalizeTab(this.defaultTab)),(t.appId&&!t.appId.firstChange||t.apiBase&&!t.apiBase.firstChange)&&this.refresh()}ngOnDestroy(){this.refreshSub?.unsubscribe()}setTab(t){this.activeTab=this.normalizeTab(t),"git"!==t||this.gitStatus||this.loadGitStatus(),"deploy"!==t||this.deployOperations.length||this.loadDeployHistory()}refresh(){this.normalizedAppId?(this.loadJobs(),this.loadDeployHistory(),this.loadGitStatus()):this.errorMessage="AICoder app id is missing."}selectJob(t){this.selectedJob=t,this.loadJobLogs(t._id)}submitRequest(){if(!this.normalizedAppId||this.isSubmitting)return;const t=String(this.requestMessage||"").trim();if(!t)return void(this.errorMessage="Describe what you want changed before starting the chat.");this.isSubmitting=!0,this.clearMessages();const e=this.isReadOnlyQuestion(t),r=e?`/api/ai-coder/apps/${this.normalizedAppId}/questions`:`/api/ai-coder/apps/${this.normalizedAppId}/requests`;this.api.post(this.apiBase,r,{title:String(this.requestTitle||"").trim(),message:t,workflow_mode:this.workflowMode,request_kind:e?"question":this.requestKind,data_change_policy:this.dataChangePolicy,deployment_target:this.deploymentTarget,evidence_notes:this.evidenceNotes,qa_focus:this.qaFocus,risk_notes:this.riskNotes},this.normalizedAppToken).subscribe({next:t=>{if(this.successMessage=e?"AI answered your question.":"AI chat started.",this.requestTitle="",this.requestMessage="",t?.job){const e=this.normalizeJob(t.job);this.jobs=[e,...this.jobs.filter(t=>t._id!==e._id)],this.selectJob(e)}},error:t=>this.handleError(t,"Could not start the AI chat."),complete:()=>this.isSubmitting=!1})}sendChatMessage(){const t=this.selectedJobId,e=String(this.chatMessage||"").trim();if(!t||!e||this.isSubmitting)return;this.isSubmitting=!0,this.clearMessages();const r=this.selectedJobIsQuestionOnly?`/api/ai-coder/jobs/${t}/question`:`/api/ai-coder/jobs/${t}/prompt`;this.api.post(this.apiBase,r,{message:e},this.normalizedAppToken).subscribe({next:t=>{this.successMessage=this.selectedJobIsQuestionOnly?"AI answered your question.":"Message sent to AICoder.",this.chatMessage="",t?.job&&(this.mergeJob(t.job),this.selectJob(t.job))},error:t=>this.handleError(t,"Could not send the message."),complete:()=>this.isSubmitting=!1})}approveSelectedJob(){this.runJobAction("approve","Approved.")}createPullRequest(){this.runJobAction("create-pr","Test review started.")}publishSelectedJob(){this.runJobAction("publish","Approved for live deploy.")}sendToTestSite(){this.canSendSelectedJobToTestSite&&this.runJobAction("test-deploy","Test site is ready.")}approveAndDeployLive(){this.canApproveSelectedJobLive&&window.confirm("Put this tested version live now? The temporary test site will be removed after this starts.")&&this.runJobAction("publish","This version is being put live.")}deleteSelectedChat(){const t=this.selectedJobId;t&&!this.isSubmitting&&window.confirm("Delete this AI chat? Any temporary test site for this chat will be removed too.")&&(this.isSubmitting=!0,this.clearMessages(),this.api.delete(this.apiBase,`/api/ai-coder/jobs/${t}`,this.normalizedAppToken).subscribe({next:()=>{this.successMessage="AI chat deleted.",this.jobs=this.jobs.filter(e=>e._id!==t),this.selectedJob=this.jobs[0]||null,this.jobLogs=[],this.selectedJob&&this.loadJobLogs(this.selectedJob._id)},error:t=>this.handleError(t,"Could not delete the AI chat."),complete:()=>this.isSubmitting=!1}))}queueDeploy(t){this.normalizedAppId&&!this.isSubmitting&&(this.isSubmitting=!0,this.clearMessages(),this.api.post(this.apiBase,`/api/ai-coder/apps/${this.normalizedAppId}/deploy`,{deploy_client:"full"===t||"client"===t,deploy_backend:"full"===t||"backend"===t,clear_destination:!0,invalidate:!0},this.normalizedAppToken).subscribe({next:()=>{this.successMessage="full"===t?"Full deploy queued.":("client"===t?"Client":"Server")+" deploy queued.",this.loadDeployHistory()},error:t=>this.handleError(t,"Could not queue deploy."),complete:()=>this.isSubmitting=!1}))}openPr(t=this.selectedJob){const e=this.resolvePrUrl(t);e&&window.open(e,"_blank","noopener")}openTestSite(){const t=this.selectedJobTestSiteUrl;t&&window.open(t,"_blank","noopener")}loadJobs(){this.normalizedAppId&&(this.isLoading=!0,this.api.get(this.apiBase,`/api/ai-coder/apps/${this.normalizedAppId}/jobs`,this.normalizedAppToken).subscribe({next:t=>{const e=this.selectedJobId;this.jobs=(t?.jobs||[]).map(t=>this.normalizeJob(t));const r=e&&this.jobs.find(t=>t._id===e)||null;r?(this.selectedJob=r,this.loadJobLogs(r._id)):!this.selectedJob&&this.visibleJobs.length&&this.selectJob(this.visibleJobs[0])},error:t=>this.handleError(t,"Could not load AI chats."),complete:()=>this.isLoading=!1}))}loadJobLogs(t){const e=String(t||"").trim();e?this.api.get(this.apiBase,`/api/ai-coder/jobs/${e}/logs?limit=80`,this.normalizedAppToken).subscribe({next:t=>this.jobLogs=this.buildTimeline(this.selectedJob,t?.logs||[]),error:t=>this.handleError(t,"Could not load chat history.")}):this.jobLogs=[]}loadDeployHistory(){this.normalizedAppId&&this.api.get(this.apiBase,`/api/ai-coder/apps/${this.normalizedAppId}/deploy/history`,this.normalizedAppToken).subscribe({next:t=>{this.deployOperations=t?.operations||[],this.deployJobs=t?.jobs||t?.deploy_jobs||[]},error:t=>this.handleError(t,"Could not load deploy history.")})}loadGitStatus(){this.normalizedAppId&&this.api.get(this.apiBase,`/api/ai-coder/apps/${this.normalizedAppId}/git`,this.normalizedAppToken).subscribe({next:t=>this.gitStatus=t?.status||t||null,error:t=>this.handleError(t,"Could not load git status.")})}get normalizedAppId(){return String(this.appId||"").trim()}get selectedJobId(){return String(this.selectedJob?._id||"").trim()}get normalizedAppToken(){return String(this.appToken||"").trim()}get activeDeploys(){return this.deployOperations.filter(t=>"In Progress"===String(t?.status||"").trim())}get recentDeploys(){return this.deployOperations.filter(t=>"In Progress"!==String(t?.status||"").trim()).slice(0,6)}get visibleJobs(){return this.computeVisibleJobs(this.jobs)}get hasSelectedJob(){return!!this.selectedJob}get selectedJobIsWorking(){return this.isActiveJob(this.selectedJob)}get selectedJobIsQuestionOnly(){return this.isQuestionOnlyJob(this.selectedJob)}get selectedJobHasChanges(){return this.hasJobChanges(this.selectedJob)}get selectedJobHasReview(){return!!this.resolvePrUrl(this.selectedJob)}get selectedJobTestSiteUrl(){return this.resolveTestSiteUrl(this.selectedJob)}get canSendSelectedJobToTestSite(){return!!this.selectedJobId&&!this.isSubmitting&&!this.selectedJobIsWorking&&this.selectedJobHasChanges&&!this.selectedJobTestSiteUrl}get canApproveSelectedJobLive(){return!!this.selectedJobId&&!this.isSubmitting&&!this.selectedJobIsWorking&&this.selectedJobHasChanges&&(!!this.selectedJobTestSiteUrl||this.selectedJobHasReview)}get selectedJobNextStepMessage(){return this.selectedJob?this.selectedJobIsQuestionOnly?"":this.selectedJobIsWorking?"I am working on this. I will update this chat when there is something ready for you to review.":this.selectedJobTestSiteUrl?"Your test site is ready. Open it and check the change before putting it live.":this.selectedJobHasReview?"A test version is ready. Open it first, then approve it when everything looks right.":this.selectedJobHasChanges?"The update is ready. Send it to the test site when you are ready.":this.isFailedJob(this.selectedJob)?"This needs your attention before it can continue. Add a reply with what you want to try next.":"This chat will update when there is something new.":""}isActiveJob(t){if(this.isQuestionOnlyJob(t)&&"COMPLETE"===String(t?.phase||"").toUpperCase())return!1;const e=String(t?.status||"").toLowerCase(),r=String(t?.phase||t?.stage||"").toLowerCase();return!(e.includes("fail")||e.includes("error")||e.includes("complete")||e.includes("passed"))&&(!r.includes("complete")&&!r.includes("review")&&("queued"===e||"in progress"===e||"running"===e||"pending"===e||["discovery","planning","execution","linting","building"].includes(r)))}resolvePrUrl(t){return String(t?.pr_url||t?.pull_request_url||"").trim()}resolveTestSiteUrl(t){return String(t?.testSiteUrl||t?.test_website_url||t?.test_url||"").trim()}friendlyJobStatus(t){if(this.isQuestionOnlyJob(t))return this.isActiveJob(t)?"AI is answering":"Answered";const e=String(t?.status||t?.stage||"").trim().toLowerCase(),r=String(t?.phase||"").trim().toLowerCase();return e||r?e.includes("complete")||e.includes("passed")||r.includes("complete")?this.resolveTestSiteUrl(t)?"Test site ready":this.resolvePrUrl(t)?"Ready to test":"Ready to review":e.includes("fail")||e.includes("error")?"Needs attention":e.includes("progress")||e.includes("running")||e.includes("pending")||e.includes("queued")?"AI is working":e.includes("pause")||e.includes("stopped")?"Paused":this.toTitle(e||r):"Waiting to start"}friendlyDeployStatus(t){const e=String(t?.status||"").trim(),r=String(t?.type||"").trim();if(!e&&!r)return"Website update";return`${r?this.toTitle(r.replace(/ai coder/gi,"").replace(/backend/gi,"server")):"Website update"}${e?` - ${e}`:""}`}formatDate(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);return Number.isNaN(e.getTime())?String(t):e.toLocaleString()}buildTimeline(t,e){const r=[],i=this.isQuestionOnlyJob(t),a=t=>{const e=this.sanitizeUserFacingMessage(t.message);if(!e)return;const i=`${t.role}:${t.label}:${t.date||""}:${e}`;r.some(t=>t.id===i||t.message===e)||r.push({...t,id:i,message:e})};(Array.isArray(t?.conversation)&&t?.conversation||[]).forEach((e,r)=>{const s="assistant"===String(e?.role||"").toLowerCase()?"assistant":"user",o="user"===s?this.extractUserRequest(e?.message||""):String(e?.message||"");a({role:s,label:"user"===s?"You":"Assistant",message:o,date:this.formatDate(e?.timestamp||e?.createdAt),active:!1}),0===r&&"user"===s&&this.isActiveJob(t)&&a({role:"assistant",label:"Assistant",message:i?"I am checking this and will answer without changing anything.":"I am working on this. I will update this chat when there is something ready for you to review.",date:"",active:!0})});const s=this.extractQuestionAnswerFromSummary(t);return s&&a({role:"assistant",label:"Assistant",message:s,date:this.formatDate(t?.updatedAt),active:!1}),!r.length&&this.isActiveJob(t)&&a({role:"assistant",label:"Assistant",message:i?"I am reading this and will answer shortly.":"I am working on this. I will update this chat when there is something ready for you to review.",date:"",active:!0}),r}extractUserRequest(t){const e=String(t||"").trim(),r=e.match(/(?:^|\n)Request:\s*([\s\S]*?)(?:\n\n|(?:^|\n)AICoder case runner contract:|$)/i);return r?.[1]?r[1].trim():e.split("\n").filter(t=>!t.match(/^(This is a change request|App:|Template:|App root:|AICoder|Case-building|Data safety|QA playbook|Branch, PR|Constraints:|- )/i)).join("\n").trim()}sanitizeUserFacingMessage(t){const e=String(t||"").replace(/```[\s\S]*?```/g,"[details hidden]").replace(/^\s*\[[^\]]+\]\s*/,"").replace(/\/var\/ai-workspace\/[^\s)]+/g,"the app workspace").replace(/ResolveIO\/aicoder-[\w-]+/g,"the app repository").replace(/apps\/[a-f0-9]{24}/gi,"the app").replace(/[a-f0-9]{24}/gi,"the app").replace(/\[([^\]]+)\]\([^)]+\)/g,(t,e)=>this.isInternalText(e)?"the app":e).replace(/\[([^\]]+)\]/g,(t,e)=>this.isInternalText(e)?"":e).trim();if(!e||this.isInternalOnlyMessage(e))return"";let r=!1;return e.split(/\r?\n/).map(t=>this.cleanUserFacingLine(t)).filter(t=>{const e=t.trim();return!!e&&(!this.isInternalLine(e)||!(!/^verification\s*:/i.test(e)||r)&&(r=!0,!0))}).map(t=>/^verification\s*:/i.test(t)?"I checked the update after making the change.":t).join("\n").replace(/\n{3,}/g,"\n\n").trim()}cleanUserFacingLine(t){return String(t||"").replace(/^\s*[-*]\s+/,"").replace(/^\[[^\]]+\]\s*/,"").replace(/^(targeted ui maintenance change|maintenance change|implementation update|change summary|summary)\s*/i,"").replace(/`([^`]+)`/g,(t,e)=>this.isInternalText(e)?"the app":e).replace(/\btop-nav\b/gi,"top navigation").replace(/\s+from the app\b[\s\S]*$/i,".").replace(/\s+/g," ").trim()}isInternalOnlyMessage(t){const e=String(t||"").toLowerCase();return e.includes("deterministic review")||e.includes("change request final review")||e.includes("cycle 1 summary")||e.includes("gates ")||e.includes("lint:pass")||e.includes("build:pass")||e.includes("review:pass")||e.includes("validate:pass")||e.includes("modified files captured")}isInternalLine(t){const e=String(t||"").toLowerCase();return/^verification\s*:/i.test(t)||this.isInternalText(t)||e.includes("grep")||e.includes("npm ")||e.includes("lint")||e.includes("build-dev")||e.includes("build-prod")||e.includes("workspace")||e.includes("routerlink")||e.includes("navtabs")||e.includes("fixtures")||e.includes("migrations")||e.includes("schemas")||e.includes("server flows")||e.includes("package files")||e.includes("deterministic review")||e.includes("cycle ")||e.includes("gates ")}isInternalText(t){const e=String(t||"").trim();return!!e&&(/(^|[\s"'(])(?:\.?\/)?[\w.-]+\/[\w./-]+/.test(e)||/\b[\w.-]+\.(ts|tsx|js|jsx|html|scss|css|json|md|yml|yaml|lock)\b/i.test(e)||/\b(package\.json|package-lock\.json|node_modules|app workspace|app repository|git|github|branch|pull request|pr_url)\b/i.test(e)||/\b(npm|grep|routerlink|navtabs|typescript|angular|nodejs|codebuild|s3:\/\/)\b/i.test(e))}computeVisibleJobs(t){const e=new Set,r=[];return(t||[]).forEach(t=>{const i=this.jobDisplayKey(t);e.has(i)||(e.add(i),r.push(t))}),r}jobDisplayKey(t){const e=(Array.isArray(t?.conversation)?t.conversation:[]).find(t=>"assistant"!==String(t?.role||"").toLowerCase())?.message||"";return String(`${t?.title||""} ${e||t?.description||""}`||t?._id||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim().slice(0,180)}hasJobChanges(t){return!this.isQuestionOnlyJob(t)&&this.hasJobChangesWithoutQuestionGuard(t)}hasJobChangesWithoutQuestionGuard(t){const e=t?.artifacts?.modifiedFiles||{},r=t?.artifacts?.diffs||{};return Object.keys(e).length>0||Object.keys(r).length>0}isQuestionOnlyJob(t){return!0===t?.runPolicy?.questionOnly||String(t?.responseSummary||"").includes("AICODER_QUESTION_ONLY")||this.isLikelyQuestionChat(t)}isLikelyQuestionChat(t){if(!t||this.hasJobChangesWithoutQuestionGuard(t)||this.resolvePrUrl(t))return!1;const e=(Array.isArray(t.conversation)?t.conversation:[]).find(t=>"assistant"!==String(t?.role||"").toLowerCase())?.message||"";return this.isReadOnlyQuestion(String(e||t.description||t.title||""))}extractQuestionAnswerFromSummary(t){const e=String(t?.responseSummary||"").trim();return e.includes("AICODER_QUESTION_ONLY")?e.replace(/^AICODER_QUESTION_ONLY\s*/i,"").trim():""}isReadOnlyQuestion(t){const e=String(t||"").trim().toLowerCase();if(!e)return!1;if(/\b(fix|change|update|add|remove|delete|create|build|make|implement|deploy|merge|push|edit|replace|rename|turn on|turn off|enable|disable|set|move)\b/i.test(e))return!1;return e.includes("?")||/(^|\b)(what|why|how|where|when|who|which|explain|describe|tell me|walk me through|show me|does|do|is|are|can i|can you)\b/i.test(e)}isFailedJob(t){const e=String(t?.status||t?.phase||"").toLowerCase();return e.includes("fail")||e.includes("error")||e.includes("blocked")}runJobAction(t,e){const r=this.selectedJobId;r&&!this.isSubmitting&&(this.isSubmitting=!0,this.clearMessages(),this.api.post(this.apiBase,`/api/ai-coder/jobs/${r}/${t}`,{},this.normalizedAppToken).subscribe({next:t=>{this.successMessage=e,t?.job&&(this.mergeJob(t.job),this.selectJob(t.job))},error:e=>this.handleError(e,`Could not ${t.replace("-"," ")}.`),complete:()=>this.isSubmitting=!1}))}mergeJob(t){const e=this.normalizeJob(t);this.jobs=[e,...this.jobs.filter(t=>t._id!==e._id)],this.selectedJob=e}normalizeJob(t){return{...t,_id:String(t?._id||"").trim(),title:String(t?.title||"AI Chat").trim(),status:String(t?.status||"").trim()||"Queued"}}normalizeTab(t){return"deploy"===t||"git"===t?t:"chats"}toTitle(t){return String(t||"").replace(/[_-]+/g," ").replace(/\s+/g," ").trim().split(" ").filter(Boolean).map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")||"Waiting to start"}startAutoRefresh(){this.refreshSub?.unsubscribe(),this.autoRefresh&&(this.refreshSub=interval(15e3).subscribe(()=>{(this.jobs.some(t=>this.isActiveJob(t))||this.activeDeploys.length)&&this.refresh()}))}clearMessages(){this.errorMessage="",this.successMessage=""}handleError(t,e){this.errorMessage=String(t?.error?.message||t?.message||e),this.successMessage="",this.isSubmitting=!1,this.isLoading=!1}static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchComponent,deps:[{token:AICoderWorkbenchApiService}],target:i0.ɵɵFactoryTarget.Component});static"ɵcmp"=i0.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"21.1.2",type:AICoderWorkbenchComponent,isStandalone:!1,selector:"resolveio-client-lib-aicoder-workbench",inputs:{appId:"appId",appName:"appName",apiBase:"apiBase",appToken:"appToken",defaultTab:"defaultTab",compact:"compact",showHeader:"showHeader",autoRefresh:"autoRefresh"},usesOnChanges:!0,ngImport:i0,template:'<section class="aicoder-workbench" [class.aicoder-workbench--compact]="compact">\n\t@if (showHeader) {\n\t\t<header class="aicoder-workbench__header">\n\t\t\t<div>\n\t\t\t\t<p>AI Chats</p>\n\t\t\t\t<h2>{{ appName || \'AICoder App\' }}</h2>\n\t\t\t</div>\n\t\t\t<button type="button" class="aicoder-workbench__ghost" (click)="refresh()" [disabled]="isLoading || isSubmitting">Refresh</button>\n\t\t</header>\n\t}\n\n\t@if (errorMessage) {\n\t\t<div class="aicoder-workbench__notice aicoder-workbench__notice--error">{{ errorMessage }}</div>\n\t}\n\t@if (successMessage) {\n\t\t<div class="aicoder-workbench__notice aicoder-workbench__notice--success">{{ successMessage }}</div>\n\t}\n\n\t<div class="aicoder-workbench__intro">\n\t\t<div>\n\t\t\t<p class="aicoder-workbench__eyebrow">AI Assistant</p>\n\t\t\t<h3>Ask for help or tell us what to change.</h3>\n\t\t\t<p>Ask questions in plain English, or request updates. AI will answer, guide you, or prepare changes when there is work to do.</p>\n\t\t</div>\n\t\t<button type="button" class="aicoder-workbench__ghost" (click)="refresh()" [disabled]="isLoading || isSubmitting">Refresh Chats</button>\n\t</div>\n\n\t<div class="aicoder-workbench__layout">\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--compose">\n\t\t\t<h3>New AI Chat</h3>\n\t\t\t<input class="aicoder-workbench__input" [(ngModel)]="requestTitle" placeholder="Short name for this chat" />\n\t\t\t<textarea class="aicoder-workbench__textarea" rows="6" [(ngModel)]="requestMessage" placeholder="Ask a question, explain what is confusing, or describe what you want changed."></textarea>\n\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="submitRequest()" [disabled]="isSubmitting || !requestMessage.trim()">\n\t\t\t\tStart AI Chat\n\t\t\t</button>\n\t\t</section>\n\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--list">\n\t\t\t<div class="aicoder-workbench__panel-head">\n\t\t\t\t<h3>Your AI Chats</h3>\n\t\t\t\t<span>{{ visibleJobs.length }}</span>\n\t\t\t</div>\n\t\t\t@if (!jobs.length && !isLoading) {\n\t\t\t\t<p class="aicoder-workbench__empty">No AI chats yet.</p>\n\t\t\t}\n\t\t\t@if (isLoading && !jobs.length) {\n\t\t\t\t<p class="aicoder-workbench__empty">Loading AI chats...</p>\n\t\t\t}\n\t\t\t<div class="aicoder-workbench__chat-list">\n\t\t\t\t@for (job of visibleJobs; track job._id) {\n\t\t\t\t\t<button type="button" class="aicoder-workbench__chat" [class.is-active]="selectedJob?._id === job._id" (click)="selectJob(job)">\n\t\t\t\t\t\t<strong>{{ job.title || \'AI Chat\' }}</strong>\n\t\t\t\t\t\t<span>{{ friendlyJobStatus(job) }}</span>\n\t\t\t\t\t\t<small>{{ formatDate(job.updatedAt || job.createdAt) }}</small>\n\t\t\t\t\t</button>\n\t\t\t\t}\n\t\t\t</div>\n\t\t</section>\n\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--detail">\n\t\t\t@if (selectedJob) {\n\t\t\t\t<div class="aicoder-workbench__detail-head">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<p class="aicoder-workbench__eyebrow">Open Chat</p>\n\t\t\t\t\t\t<h3>{{ selectedJob.title || \'AI Chat\' }}</h3>\n\t\t\t\t\t\t<span class="aicoder-workbench__status">{{ friendlyJobStatus(selectedJob) }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<button type="button" class="aicoder-workbench__danger" (click)="deleteSelectedChat()" [disabled]="isSubmitting">Delete Chat</button>\n\t\t\t\t</div>\n\n\t\t\t\t@if (selectedJobTestSiteUrl || canSendSelectedJobToTestSite || canApproveSelectedJobLive || selectedJobHasReview) {\n\t\t\t\t\t<div class="aicoder-workbench__actions" aria-label="AI chat actions">\n\t\t\t\t\t\t@if (selectedJobTestSiteUrl) {\n\t\t\t\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="openTestSite()" [disabled]="isSubmitting">\n\t\t\t\t\t\t\t\tOpen Test Site\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (canSendSelectedJobToTestSite) {\n\t\t\t\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="sendToTestSite()" [disabled]="isSubmitting">\n\t\t\t\t\t\t\t\tSend to Test Site\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (canApproveSelectedJobLive) {\n\t\t\t\t\t\t\t<button type="button" (click)="approveAndDeployLive()" [disabled]="isSubmitting">\n\t\t\t\t\t\t\t\tPut This Version Live\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (selectedJobHasReview) {\n\t\t\t\t\t\t\t<button type="button" (click)="openPr()" [disabled]="!resolvePrUrl(selectedJob)">\n\t\t\t\t\t\t\t\tOpen Review\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t\t@else if (selectedJobNextStepMessage) {\n\t\t\t\t\t<div class="aicoder-workbench__next-step">\n\t\t\t\t\t\t@if (selectedJobIsWorking) {\n\t\t\t\t\t\t\t<span class="aicoder-workbench__spinner" aria-hidden="true"></span>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t<p>{{ selectedJobNextStepMessage }}</p>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\n\t\t\t\t<div class="aicoder-workbench__history">\n\t\t\t\t\t@if (!jobLogs.length) {\n\t\t\t\t\t\t<div class="aicoder-workbench__thinking">\n\t\t\t\t\t\t\t<span class="aicoder-workbench__spinner" aria-hidden="true"></span>\n\t\t\t\t\t\t\t<p>AI is getting started.</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t\t@for (log of jobLogs; track log.id) {\n\t\t\t\t\t\t<div class="aicoder-workbench__message" [class.aicoder-workbench__message--user]="log.role === \'user\'" [class.aicoder-workbench__message--assistant]="log.role === \'assistant\'" [class.aicoder-workbench__message--system]="log.role === \'system\'" [class.is-active]="log.active">\n\t\t\t\t\t\t\t<div class="aicoder-workbench__message-meta">\n\t\t\t\t\t\t\t\t<strong>{{ log.label }}</strong>\n\t\t\t\t\t\t\t\t@if (log.date) {\n\t\t\t\t\t\t\t\t\t<span>{{ log.date }}</span>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<p>{{ log.message }}</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\n\t\t\t\t<label class="aicoder-workbench__reply">\n\t\t\t\t\t<span>Reply to AI</span>\n\t\t\t\t\t<textarea class="aicoder-workbench__textarea" rows="4" [(ngModel)]="chatMessage" [placeholder]="selectedJobIsQuestionOnly ? \'Ask a follow-up question.\' : \'Add more details, ask for another change, or answer a question from AI.\'"></textarea>\n\t\t\t\t</label>\n\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="sendChatMessage()" [disabled]="!selectedJobId || !chatMessage.trim() || isSubmitting">\n\t\t\t\t\tSend Reply\n\t\t\t\t</button>\n\t\t\t}\n\t\t\t@else {\n\t\t\t\t<div class="aicoder-workbench__blank">\n\t\t\t\t\t<h3>Select an AI chat</h3>\n\t\t\t\t\t<p>Open a chat from the list or start a new one.</p>\n\t\t\t\t</div>\n\t\t\t}\n\t\t</section>\n\t</div>\n\n\t@if (activeDeploys.length || recentDeploys.length) {\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--updates">\n\t\t\t<div class="aicoder-workbench__panel-head">\n\t\t\t\t<h3>Website Updates</h3>\n\t\t\t\t<span>{{ activeDeploys.length ? \'Running now\' : \'Recent\' }}</span>\n\t\t\t</div>\n\t\t\t<div class="aicoder-workbench__updates">\n\t\t\t\t@for (op of activeDeploys; track op.id || op.jobId) {\n\t\t\t\t\t<div class="aicoder-workbench__update">\n\t\t\t\t\t\t<strong>{{ friendlyDeployStatus(op) }}</strong>\n\t\t\t\t\t\t<span>{{ op.backend_deploy_step || op.aws_build_current_phase || op.message || \'Working on the update\' }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t\t@for (op of recentDeploys; track op.id || op.jobId) {\n\t\t\t\t\t<div class="aicoder-workbench__update">\n\t\t\t\t\t\t<strong>{{ friendlyDeployStatus(op) }}</strong>\n\t\t\t\t\t\t<span>{{ op.message || op.backend_deploy_step || \'Update finished\' }}</span>\n\t\t\t\t\t\t<small>{{ formatDate(op.updatedAt || op.createdAt) }}</small>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t</section>\n\t}\n</section>\n',styles:['.aicoder-workbench{--aicoder-accent: #2f7d73;--aicoder-accent-dark: #155e57;--aicoder-border: #d8e2ec;--aicoder-ink: #1f2933;--aicoder-muted: #64748b;--aicoder-soft: #f5f8fb;display:flex;flex-direction:column;gap:1rem;color:var(--aicoder-ink)}.aicoder-workbench__header,.aicoder-workbench__intro,.aicoder-workbench__panel-head,.aicoder-workbench__detail-head,.aicoder-workbench__actions{display:flex;align-items:center;justify-content:space-between;gap:1rem}.aicoder-workbench__header p,.aicoder-workbench__eyebrow{margin:0 0 .25rem;color:var(--aicoder-accent-dark);font-size:.75rem;font-weight:800;letter-spacing:0;text-transform:uppercase}.aicoder-workbench__header h2,.aicoder-workbench__intro h3,.aicoder-workbench__panel h3{margin:0;font-weight:800;line-height:1.2}.aicoder-workbench__header h2{font-size:1.35rem}.aicoder-workbench__intro{border:1px solid var(--aicoder-border);border-radius:8px;background:#fff;padding:1rem}.aicoder-workbench__intro h3{font-size:1.25rem}.aicoder-workbench__intro p:not(.aicoder-workbench__eyebrow),.aicoder-workbench__blank p,.aicoder-workbench__empty{margin:.35rem 0 0;color:var(--aicoder-muted);line-height:1.45}.aicoder-workbench__layout{display:grid;grid-template-columns:minmax(260px,.75fr) minmax(320px,1.35fr);grid-template-areas:"compose detail" "list detail";gap:1rem;align-items:start}.aicoder-workbench__panel{min-width:0;border:1px solid var(--aicoder-border);border-radius:8px;background:#fff;padding:1rem}.aicoder-workbench__panel--compose{grid-area:compose}.aicoder-workbench__panel--list{grid-area:list}.aicoder-workbench__panel--detail{grid-area:detail}.aicoder-workbench__panel--updates{display:grid;gap:.75rem}.aicoder-workbench__panel-head span,.aicoder-workbench__status{display:inline-flex;align-items:center;border-radius:999px;background:#e7f4f0;color:var(--aicoder-accent-dark);font-size:.78rem;font-weight:800;padding:.25rem .6rem}.aicoder-workbench__next-step,.aicoder-workbench__thinking{display:flex;align-items:center;gap:.65rem;border:1px solid #dbe7ef;border-radius:8px;background:#f7fafc;color:var(--aicoder-muted);margin-top:1rem;padding:.85rem}.aicoder-workbench__next-step p,.aicoder-workbench__thinking p{margin:0;line-height:1.4}.aicoder-workbench__spinner{display:inline-block;width:1rem;height:1rem;border:2px solid #bfd2df;border-top-color:var(--aicoder-accent);border-radius:999px;flex:0 0 auto;animation:aicoder-workbench-spin .85s linear infinite}.aicoder-workbench__input,.aicoder-workbench__textarea{width:100%;border:1px solid #cbd5e1;border-radius:6px;background:#fff;color:var(--aicoder-ink);padding:.7rem .8rem}.aicoder-workbench__textarea{resize:vertical}.aicoder-workbench__input,.aicoder-workbench__textarea{margin:.75rem 0}.aicoder-workbench__ghost,.aicoder-workbench__primary,.aicoder-workbench__danger,.aicoder-workbench__actions button{border:1px solid #cbd5e1;border-radius:6px;background:#fff;color:var(--aicoder-ink);cursor:pointer;font:inherit;font-weight:800;min-height:40px;padding:.6rem .85rem;text-decoration:none}.aicoder-workbench__primary{border-color:var(--aicoder-accent);background:var(--aicoder-accent);color:#fff}.aicoder-workbench__danger{border-color:#fecaca;color:#991b1b}.aicoder-workbench button:disabled{cursor:not-allowed;opacity:.55}.aicoder-workbench__chat-list,.aicoder-workbench__history,.aicoder-workbench__updates{display:flex;flex-direction:column;gap:.5rem}.aicoder-workbench__chat-list,.aicoder-workbench__history{max-height:520px;overflow:auto}.aicoder-workbench__chat,.aicoder-workbench__message,.aicoder-workbench__update{display:flex;flex-direction:column;align-items:flex-start;gap:.25rem;width:100%;border:1px solid #e2e8f0;border-radius:7px;background:var(--aicoder-soft);padding:.8rem;text-align:left}.aicoder-workbench__message{background:#fff}.aicoder-workbench__message--user{border-color:#c9d8e6;background:#f8fafc}.aicoder-workbench__message--assistant{border-color:#b7ded3;background:#f4fbf9}.aicoder-workbench__message--system{background:#fbfdff}.aicoder-workbench__message.is-active{border-color:var(--aicoder-accent)}.aicoder-workbench__message-meta{display:flex;align-items:center;justify-content:space-between;gap:.75rem;width:100%;color:var(--aicoder-muted);font-size:.8rem;line-height:1.35}.aicoder-workbench__message-meta strong{color:var(--aicoder-ink);font-size:.82rem}.aicoder-workbench__chat{cursor:pointer}.aicoder-workbench__chat.is-active{border-color:var(--aicoder-accent);background:#ecfdf5}.aicoder-workbench__chat span,.aicoder-workbench__chat small,.aicoder-workbench__message span,.aicoder-workbench__update span,.aicoder-workbench__update small{color:var(--aicoder-muted);font-size:.82rem;line-height:1.35}.aicoder-workbench__message p{margin:0;white-space:pre-wrap;overflow-wrap:anywhere}.aicoder-workbench__history{min-height:260px;margin:.75rem 0}.aicoder-workbench__actions{flex-wrap:wrap;justify-content:flex-start;margin-top:1rem}.aicoder-workbench__reply{display:block;margin-top:1rem}.aicoder-workbench__reply span{display:block;color:var(--aicoder-muted);font-size:.78rem;font-weight:800;text-transform:uppercase}.aicoder-workbench__blank{display:grid;min-height:280px;place-content:center;text-align:center}.aicoder-workbench__notice{border-radius:6px;padding:.75rem .9rem;font-weight:800}.aicoder-workbench__notice--error{border:1px solid #fecaca;background:#fff1f2;color:#991b1b}.aicoder-workbench__notice--success{border:1px solid #bbf7d0;background:#f0fdf4;color:#166534}@keyframes aicoder-workbench-spin{to{transform:rotate(360deg)}}.aicoder-workbench--compact .aicoder-workbench__layout{grid-template-columns:minmax(240px,.7fr) minmax(320px,1.3fr)}@media(max-width:980px){.aicoder-workbench__layout,.aicoder-workbench--compact .aicoder-workbench__layout{grid-template-columns:1fr;grid-template-areas:"compose" "list" "detail"}.aicoder-workbench__header,.aicoder-workbench__intro,.aicoder-workbench__detail-head{align-items:stretch;flex-direction:column}.aicoder-workbench__actions button,.aicoder-workbench__primary,.aicoder-workbench__ghost,.aicoder-workbench__danger{width:100%}}\n'],dependencies:[{kind:"directive",type:i2$1.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:i2$1.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:i2$1.NgModel,selector:"[ngModel]:not([formControlName]):not([formControl])",inputs:["name","disabled","ngModel","ngModelOptions"],outputs:["ngModelChange"],exportAs:["ngModel"]}]})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchComponent,decorators:[{type:Component,args:[{selector:"resolveio-client-lib-aicoder-workbench",standalone:!1,template:'<section class="aicoder-workbench" [class.aicoder-workbench--compact]="compact">\n\t@if (showHeader) {\n\t\t<header class="aicoder-workbench__header">\n\t\t\t<div>\n\t\t\t\t<p>AI Chats</p>\n\t\t\t\t<h2>{{ appName || \'AICoder App\' }}</h2>\n\t\t\t</div>\n\t\t\t<button type="button" class="aicoder-workbench__ghost" (click)="refresh()" [disabled]="isLoading || isSubmitting">Refresh</button>\n\t\t</header>\n\t}\n\n\t@if (errorMessage) {\n\t\t<div class="aicoder-workbench__notice aicoder-workbench__notice--error">{{ errorMessage }}</div>\n\t}\n\t@if (successMessage) {\n\t\t<div class="aicoder-workbench__notice aicoder-workbench__notice--success">{{ successMessage }}</div>\n\t}\n\n\t<div class="aicoder-workbench__intro">\n\t\t<div>\n\t\t\t<p class="aicoder-workbench__eyebrow">AI Assistant</p>\n\t\t\t<h3>Ask for help or tell us what to change.</h3>\n\t\t\t<p>Ask questions in plain English, or request updates. AI will answer, guide you, or prepare changes when there is work to do.</p>\n\t\t</div>\n\t\t<button type="button" class="aicoder-workbench__ghost" (click)="refresh()" [disabled]="isLoading || isSubmitting">Refresh Chats</button>\n\t</div>\n\n\t<div class="aicoder-workbench__layout">\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--compose">\n\t\t\t<h3>New AI Chat</h3>\n\t\t\t<input class="aicoder-workbench__input" [(ngModel)]="requestTitle" placeholder="Short name for this chat" />\n\t\t\t<textarea class="aicoder-workbench__textarea" rows="6" [(ngModel)]="requestMessage" placeholder="Ask a question, explain what is confusing, or describe what you want changed."></textarea>\n\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="submitRequest()" [disabled]="isSubmitting || !requestMessage.trim()">\n\t\t\t\tStart AI Chat\n\t\t\t</button>\n\t\t</section>\n\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--list">\n\t\t\t<div class="aicoder-workbench__panel-head">\n\t\t\t\t<h3>Your AI Chats</h3>\n\t\t\t\t<span>{{ visibleJobs.length }}</span>\n\t\t\t</div>\n\t\t\t@if (!jobs.length && !isLoading) {\n\t\t\t\t<p class="aicoder-workbench__empty">No AI chats yet.</p>\n\t\t\t}\n\t\t\t@if (isLoading && !jobs.length) {\n\t\t\t\t<p class="aicoder-workbench__empty">Loading AI chats...</p>\n\t\t\t}\n\t\t\t<div class="aicoder-workbench__chat-list">\n\t\t\t\t@for (job of visibleJobs; track job._id) {\n\t\t\t\t\t<button type="button" class="aicoder-workbench__chat" [class.is-active]="selectedJob?._id === job._id" (click)="selectJob(job)">\n\t\t\t\t\t\t<strong>{{ job.title || \'AI Chat\' }}</strong>\n\t\t\t\t\t\t<span>{{ friendlyJobStatus(job) }}</span>\n\t\t\t\t\t\t<small>{{ formatDate(job.updatedAt || job.createdAt) }}</small>\n\t\t\t\t\t</button>\n\t\t\t\t}\n\t\t\t</div>\n\t\t</section>\n\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--detail">\n\t\t\t@if (selectedJob) {\n\t\t\t\t<div class="aicoder-workbench__detail-head">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<p class="aicoder-workbench__eyebrow">Open Chat</p>\n\t\t\t\t\t\t<h3>{{ selectedJob.title || \'AI Chat\' }}</h3>\n\t\t\t\t\t\t<span class="aicoder-workbench__status">{{ friendlyJobStatus(selectedJob) }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<button type="button" class="aicoder-workbench__danger" (click)="deleteSelectedChat()" [disabled]="isSubmitting">Delete Chat</button>\n\t\t\t\t</div>\n\n\t\t\t\t@if (selectedJobTestSiteUrl || canSendSelectedJobToTestSite || canApproveSelectedJobLive || selectedJobHasReview) {\n\t\t\t\t\t<div class="aicoder-workbench__actions" aria-label="AI chat actions">\n\t\t\t\t\t\t@if (selectedJobTestSiteUrl) {\n\t\t\t\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="openTestSite()" [disabled]="isSubmitting">\n\t\t\t\t\t\t\t\tOpen Test Site\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (canSendSelectedJobToTestSite) {\n\t\t\t\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="sendToTestSite()" [disabled]="isSubmitting">\n\t\t\t\t\t\t\t\tSend to Test Site\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (canApproveSelectedJobLive) {\n\t\t\t\t\t\t\t<button type="button" (click)="approveAndDeployLive()" [disabled]="isSubmitting">\n\t\t\t\t\t\t\t\tPut This Version Live\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (selectedJobHasReview) {\n\t\t\t\t\t\t\t<button type="button" (click)="openPr()" [disabled]="!resolvePrUrl(selectedJob)">\n\t\t\t\t\t\t\t\tOpen Review\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t\t@else if (selectedJobNextStepMessage) {\n\t\t\t\t\t<div class="aicoder-workbench__next-step">\n\t\t\t\t\t\t@if (selectedJobIsWorking) {\n\t\t\t\t\t\t\t<span class="aicoder-workbench__spinner" aria-hidden="true"></span>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t<p>{{ selectedJobNextStepMessage }}</p>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\n\t\t\t\t<div class="aicoder-workbench__history">\n\t\t\t\t\t@if (!jobLogs.length) {\n\t\t\t\t\t\t<div class="aicoder-workbench__thinking">\n\t\t\t\t\t\t\t<span class="aicoder-workbench__spinner" aria-hidden="true"></span>\n\t\t\t\t\t\t\t<p>AI is getting started.</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t\t@for (log of jobLogs; track log.id) {\n\t\t\t\t\t\t<div class="aicoder-workbench__message" [class.aicoder-workbench__message--user]="log.role === \'user\'" [class.aicoder-workbench__message--assistant]="log.role === \'assistant\'" [class.aicoder-workbench__message--system]="log.role === \'system\'" [class.is-active]="log.active">\n\t\t\t\t\t\t\t<div class="aicoder-workbench__message-meta">\n\t\t\t\t\t\t\t\t<strong>{{ log.label }}</strong>\n\t\t\t\t\t\t\t\t@if (log.date) {\n\t\t\t\t\t\t\t\t\t<span>{{ log.date }}</span>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<p>{{ log.message }}</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\n\t\t\t\t<label class="aicoder-workbench__reply">\n\t\t\t\t\t<span>Reply to AI</span>\n\t\t\t\t\t<textarea class="aicoder-workbench__textarea" rows="4" [(ngModel)]="chatMessage" [placeholder]="selectedJobIsQuestionOnly ? \'Ask a follow-up question.\' : \'Add more details, ask for another change, or answer a question from AI.\'"></textarea>\n\t\t\t\t</label>\n\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="sendChatMessage()" [disabled]="!selectedJobId || !chatMessage.trim() || isSubmitting">\n\t\t\t\t\tSend Reply\n\t\t\t\t</button>\n\t\t\t}\n\t\t\t@else {\n\t\t\t\t<div class="aicoder-workbench__blank">\n\t\t\t\t\t<h3>Select an AI chat</h3>\n\t\t\t\t\t<p>Open a chat from the list or start a new one.</p>\n\t\t\t\t</div>\n\t\t\t}\n\t\t</section>\n\t</div>\n\n\t@if (activeDeploys.length || recentDeploys.length) {\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--updates">\n\t\t\t<div class="aicoder-workbench__panel-head">\n\t\t\t\t<h3>Website Updates</h3>\n\t\t\t\t<span>{{ activeDeploys.length ? \'Running now\' : \'Recent\' }}</span>\n\t\t\t</div>\n\t\t\t<div class="aicoder-workbench__updates">\n\t\t\t\t@for (op of activeDeploys; track op.id || op.jobId) {\n\t\t\t\t\t<div class="aicoder-workbench__update">\n\t\t\t\t\t\t<strong>{{ friendlyDeployStatus(op) }}</strong>\n\t\t\t\t\t\t<span>{{ op.backend_deploy_step || op.aws_build_current_phase || op.message || \'Working on the update\' }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t\t@for (op of recentDeploys; track op.id || op.jobId) {\n\t\t\t\t\t<div class="aicoder-workbench__update">\n\t\t\t\t\t\t<strong>{{ friendlyDeployStatus(op) }}</strong>\n\t\t\t\t\t\t<span>{{ op.message || op.backend_deploy_step || \'Update finished\' }}</span>\n\t\t\t\t\t\t<small>{{ formatDate(op.updatedAt || op.createdAt) }}</small>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t</section>\n\t}\n</section>\n',styles:['.aicoder-workbench{--aicoder-accent: #2f7d73;--aicoder-accent-dark: #155e57;--aicoder-border: #d8e2ec;--aicoder-ink: #1f2933;--aicoder-muted: #64748b;--aicoder-soft: #f5f8fb;display:flex;flex-direction:column;gap:1rem;color:var(--aicoder-ink)}.aicoder-workbench__header,.aicoder-workbench__intro,.aicoder-workbench__panel-head,.aicoder-workbench__detail-head,.aicoder-workbench__actions{display:flex;align-items:center;justify-content:space-between;gap:1rem}.aicoder-workbench__header p,.aicoder-workbench__eyebrow{margin:0 0 .25rem;color:var(--aicoder-accent-dark);font-size:.75rem;font-weight:800;letter-spacing:0;text-transform:uppercase}.aicoder-workbench__header h2,.aicoder-workbench__intro h3,.aicoder-workbench__panel h3{margin:0;font-weight:800;line-height:1.2}.aicoder-workbench__header h2{font-size:1.35rem}.aicoder-workbench__intro{border:1px solid var(--aicoder-border);border-radius:8px;background:#fff;padding:1rem}.aicoder-workbench__intro h3{font-size:1.25rem}.aicoder-workbench__intro p:not(.aicoder-workbench__eyebrow),.aicoder-workbench__blank p,.aicoder-workbench__empty{margin:.35rem 0 0;color:var(--aicoder-muted);line-height:1.45}.aicoder-workbench__layout{display:grid;grid-template-columns:minmax(260px,.75fr) minmax(320px,1.35fr);grid-template-areas:"compose detail" "list detail";gap:1rem;align-items:start}.aicoder-workbench__panel{min-width:0;border:1px solid var(--aicoder-border);border-radius:8px;background:#fff;padding:1rem}.aicoder-workbench__panel--compose{grid-area:compose}.aicoder-workbench__panel--list{grid-area:list}.aicoder-workbench__panel--detail{grid-area:detail}.aicoder-workbench__panel--updates{display:grid;gap:.75rem}.aicoder-workbench__panel-head span,.aicoder-workbench__status{display:inline-flex;align-items:center;border-radius:999px;background:#e7f4f0;color:var(--aicoder-accent-dark);font-size:.78rem;font-weight:800;padding:.25rem .6rem}.aicoder-workbench__next-step,.aicoder-workbench__thinking{display:flex;align-items:center;gap:.65rem;border:1px solid #dbe7ef;border-radius:8px;background:#f7fafc;color:var(--aicoder-muted);margin-top:1rem;padding:.85rem}.aicoder-workbench__next-step p,.aicoder-workbench__thinking p{margin:0;line-height:1.4}.aicoder-workbench__spinner{display:inline-block;width:1rem;height:1rem;border:2px solid #bfd2df;border-top-color:var(--aicoder-accent);border-radius:999px;flex:0 0 auto;animation:aicoder-workbench-spin .85s linear infinite}.aicoder-workbench__input,.aicoder-workbench__textarea{width:100%;border:1px solid #cbd5e1;border-radius:6px;background:#fff;color:var(--aicoder-ink);padding:.7rem .8rem}.aicoder-workbench__textarea{resize:vertical}.aicoder-workbench__input,.aicoder-workbench__textarea{margin:.75rem 0}.aicoder-workbench__ghost,.aicoder-workbench__primary,.aicoder-workbench__danger,.aicoder-workbench__actions button{border:1px solid #cbd5e1;border-radius:6px;background:#fff;color:var(--aicoder-ink);cursor:pointer;font:inherit;font-weight:800;min-height:40px;padding:.6rem .85rem;text-decoration:none}.aicoder-workbench__primary{border-color:var(--aicoder-accent);background:var(--aicoder-accent);color:#fff}.aicoder-workbench__danger{border-color:#fecaca;color:#991b1b}.aicoder-workbench button:disabled{cursor:not-allowed;opacity:.55}.aicoder-workbench__chat-list,.aicoder-workbench__history,.aicoder-workbench__updates{display:flex;flex-direction:column;gap:.5rem}.aicoder-workbench__chat-list,.aicoder-workbench__history{max-height:520px;overflow:auto}.aicoder-workbench__chat,.aicoder-workbench__message,.aicoder-workbench__update{display:flex;flex-direction:column;align-items:flex-start;gap:.25rem;width:100%;border:1px solid #e2e8f0;border-radius:7px;background:var(--aicoder-soft);padding:.8rem;text-align:left}.aicoder-workbench__message{background:#fff}.aicoder-workbench__message--user{border-color:#c9d8e6;background:#f8fafc}.aicoder-workbench__message--assistant{border-color:#b7ded3;background:#f4fbf9}.aicoder-workbench__message--system{background:#fbfdff}.aicoder-workbench__message.is-active{border-color:var(--aicoder-accent)}.aicoder-workbench__message-meta{display:flex;align-items:center;justify-content:space-between;gap:.75rem;width:100%;color:var(--aicoder-muted);font-size:.8rem;line-height:1.35}.aicoder-workbench__message-meta strong{color:var(--aicoder-ink);font-size:.82rem}.aicoder-workbench__chat{cursor:pointer}.aicoder-workbench__chat.is-active{border-color:var(--aicoder-accent);background:#ecfdf5}.aicoder-workbench__chat span,.aicoder-workbench__chat small,.aicoder-workbench__message span,.aicoder-workbench__update span,.aicoder-workbench__update small{color:var(--aicoder-muted);font-size:.82rem;line-height:1.35}.aicoder-workbench__message p{margin:0;white-space:pre-wrap;overflow-wrap:anywhere}.aicoder-workbench__history{min-height:260px;margin:.75rem 0}.aicoder-workbench__actions{flex-wrap:wrap;justify-content:flex-start;margin-top:1rem}.aicoder-workbench__reply{display:block;margin-top:1rem}.aicoder-workbench__reply span{display:block;color:var(--aicoder-muted);font-size:.78rem;font-weight:800;text-transform:uppercase}.aicoder-workbench__blank{display:grid;min-height:280px;place-content:center;text-align:center}.aicoder-workbench__notice{border-radius:6px;padding:.75rem .9rem;font-weight:800}.aicoder-workbench__notice--error{border:1px solid #fecaca;background:#fff1f2;color:#991b1b}.aicoder-workbench__notice--success{border:1px solid #bbf7d0;background:#f0fdf4;color:#166534}@keyframes aicoder-workbench-spin{to{transform:rotate(360deg)}}.aicoder-workbench--compact .aicoder-workbench__layout{grid-template-columns:minmax(240px,.7fr) minmax(320px,1.3fr)}@media(max-width:980px){.aicoder-workbench__layout,.aicoder-workbench--compact .aicoder-workbench__layout{grid-template-columns:1fr;grid-template-areas:"compose" "list" "detail"}.aicoder-workbench__header,.aicoder-workbench__intro,.aicoder-workbench__detail-head{align-items:stretch;flex-direction:column}.aicoder-workbench__actions button,.aicoder-workbench__primary,.aicoder-workbench__ghost,.aicoder-workbench__danger{width:100%}}\n']}]}],ctorParameters:()=>[{type:AICoderWorkbenchApiService}],propDecorators:{appId:[{type:Input}],appName:[{type:Input}],apiBase:[{type:Input}],appToken:[{type:Input}],defaultTab:[{type:Input}],compact:[{type:Input}],showHeader:[{type:Input}],autoRefresh:[{type:Input}]}});class AICoderDashboardModule{static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,deps:[],target:i0.ɵɵFactoryTarget.NgModule});static"ɵmod"=i0.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,declarations:[AICoderDashboardComponent,AICoderWorkbenchComponent],imports:[CommonModule,FormsModule,HttpClientModule],exports:[AICoderDashboardComponent,AICoderWorkbenchComponent]});static"ɵinj"=i0.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,imports:[CommonModule,FormsModule,HttpClientModule]})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,decorators:[{type:NgModule,args:[{imports:[CommonModule,FormsModule,HttpClientModule],declarations:[AICoderDashboardComponent,AICoderWorkbenchComponent],exports:[AICoderDashboardComponent,AICoderWorkbenchComponent]}]}]});export{AICoderDashboardComponent,AICoderDashboardModule,AICoderDashboardService,AICoderWorkbenchApiService,AICoderWorkbenchComponent};
1
+ import*as i0 from"@angular/core";import{Injectable,EventEmitter,Output,Input,Component,NgModule}from"@angular/core";import*as i1 from"@angular/common/http";import{HttpHeaders,HttpClientModule}from"@angular/common/http";import*as i2 from"@resolveio/client-lib-core";import{Observable,throwError,interval}from"rxjs";import{switchMap,map,tap,shareReplay}from"rxjs/operators";import*as i2$1 from"@angular/forms";import{FormsModule}from"@angular/forms";import{CommonModule}from"@angular/common";class AICoderDashboardService{buildDefaultSummary(){return{overview:{app_name:"ResolveIO App",app_status:"Active",plan_tier:"small",max_users:10},database:{name:"resolveio",host:"127.0.0.1",status:"Ready"},infrastructure:{current_tier:"small",available_tiers:["small","medium","large"],max_users:10,backend_status:"Ready"},code_changes:{release_notes:"Latest updates are live and available.",last_deployed_at:"",recent_updates:["Overview tab highlights app status and plan tier.","Infrastructure tab shows Small, Medium, and Large options.","Code Changes tab summarizes customer-facing updates."]},subscription:{current_package:"Small",current_tier:"small",package_price_label:"$99 / month",billing_status:"Paid",billing_mode:"Subscription",subscription_status:"active",current_period_end:"",manage_url:"",can_downgrade:!0,downgrade_reason:""}}}buildTierOptions(){return[{id:"small",label:"Small",description:"Great for focused teams and lighter usage."},{id:"medium",label:"Medium",description:"Balanced capacity for growing teams and workflows."},{id:"large",label:"Large",description:"Higher capacity for heavy usage and larger teams."}]}static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardService,deps:[],target:i0.ɵɵFactoryTarget.Injectable});static"ɵprov"=i0.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardService,providedIn:"root"})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardService,decorators:[{type:Injectable,args:[{providedIn:"root"}]}]});class AICoderDashboardComponent{dashboardService;summary;loading=!1;errorMessage="";activeTab="overview";tierOptions;tabChange=new EventEmitter;retry=new EventEmitter;tierChange=new EventEmitter;manageSubscription=new EventEmitter;constructor(t){this.dashboardService=t,this.summary=this.dashboardService.buildDefaultSummary(),this.tierOptions=this.dashboardService.buildTierOptions()}setTab(t){this.activeTab!==t&&this.tabChange.emit(t)}requestRetry(){this.retry.emit()}requestTierChange(t){this.tierChange.emit(t)}requestManageSubscription(){this.manageSubscription.emit(this.resolveManageSubscriptionUrl())}isCurrentTier(t){return String(this.summary?.infrastructure?.current_tier||"").trim().toLowerCase()===t}hasManageSubscriptionUrl(){return!!this.resolveManageSubscriptionUrl()}resolveManageSubscriptionUrl(){return String(this.summary?.subscription?.manage_url||"").trim()}static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardComponent,deps:[{token:AICoderDashboardService}],target:i0.ɵɵFactoryTarget.Component});static"ɵcmp"=i0.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"21.1.2",type:AICoderDashboardComponent,isStandalone:!1,selector:"resolveio-client-lib-aicoder-dashboard",inputs:{summary:"summary",loading:"loading",errorMessage:"errorMessage",activeTab:"activeTab",tierOptions:"tierOptions"},outputs:{tabChange:"tabChange",retry:"retry",tierChange:"tierChange",manageSubscription:"manageSubscription"},ngImport:i0,template:'<div class="aicoder-dashboard">\n\t<div class="aicoder-dashboard__tabs">\n\t\t<button type="button" [class.is-active]="activeTab === \'overview\'" (click)="setTab(\'overview\')">Overview</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'database\'" (click)="setTab(\'database\')">Database</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'infrastructure\'" (click)="setTab(\'infrastructure\')">Infrastructure</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'code_changes\'" (click)="setTab(\'code_changes\')">Code Changes</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'subscription\'" (click)="setTab(\'subscription\')">Subscription</button>\n\t</div>\n\n\t@if (loading) {\n\t\t<div class="aicoder-dashboard__state">Loading dashboard details...</div>\n\t} @else if (errorMessage) {\n\t\t<div class="aicoder-dashboard__state aicoder-dashboard__state--error">\n\t\t\t<span>{{ errorMessage }}</span>\n\t\t\t<button type="button" (click)="requestRetry()">Retry</button>\n\t\t</div>\n\t} @else {\n\t\t@if (activeTab === \'overview\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>App</span><strong>{{ summary.overview.app_name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.overview.app_status }}</strong></div>\n\t\t\t\t<div class="tile"><span>Tier</span><strong>{{ summary.overview.plan_tier }}</strong></div>\n\t\t\t\t<div class="tile"><span>Max users</span><strong>{{ summary.overview.max_users }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'database\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>Name</span><strong>{{ summary.database.name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Host</span><strong>{{ summary.database.host }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.database.status }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'infrastructure\') {\n\t\t\t<div class="aicoder-dashboard__tiers">\n\t\t\t\t@for (tier of tierOptions; track tier.id) {\n\t\t\t\t\t<div class="tier" [class.tier--active]="isCurrentTier(tier.id)">\n\t\t\t\t\t\t<h4>{{ tier.label }}</h4>\n\t\t\t\t\t\t<p>{{ tier.description }}</p>\n\t\t\t\t\t\t<button type="button" (click)="requestTierChange(tier.id)">Choose {{ tier.label }}</button>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'code_changes\') {\n\t\t\t<div class="aicoder-dashboard__changes">\n\t\t\t\t<p>{{ summary.code_changes.release_notes }}</p>\n\t\t\t\t<ul>\n\t\t\t\t\t@for (item of summary.code_changes.recent_updates; track item) {\n\t\t\t\t\t\t<li>{{ item }}</li>\n\t\t\t\t\t}\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'subscription\') {\n\t\t\t<div class="aicoder-dashboard__subscription">\n\t\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t\t<div class="tile"><span>Package</span><strong>{{ summary.subscription?.current_package || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Tier</span><strong>{{ summary.subscription?.current_tier || summary.overview.plan_tier || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Price</span><strong>{{ summary.subscription?.package_price_label || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Billing status</span><strong>{{ summary.subscription?.billing_status || \'Unknown\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Billing mode</span><strong>{{ summary.subscription?.billing_mode || \'Unknown\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Subscription</span><strong>{{ summary.subscription?.subscription_status || \'N/A\' }}</strong></div>\n\t\t\t\t</div>\n\t\t\t\t@if (summary.subscription?.current_period_end) {\n\t\t\t\t\t<p class="aicoder-dashboard__hint">Current period ends: {{ summary.subscription?.current_period_end }}</p>\n\t\t\t\t}\n\t\t\t\t@if (summary.subscription?.downgrade_reason) {\n\t\t\t\t\t<p class="aicoder-dashboard__hint aicoder-dashboard__hint--warning">{{ summary.subscription?.downgrade_reason }}</p>\n\t\t\t\t}\n\t\t\t\t<div class="aicoder-dashboard__subscription-actions">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype="button"\n\t\t\t\t\t\tclass="aicoder-dashboard__primary-action"\n\t\t\t\t\t\t(click)="requestManageSubscription()"\n\t\t\t\t\t\t[disabled]="!hasManageSubscriptionUrl()"\n\t\t\t\t\t>\n\t\t\t\t\t\tUpgrade Or Manage Plan\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t}\n\t}\n</div>\n',styles:[".aicoder-dashboard{--aicoder-primary: #0f4c81;font-family:inherit}.aicoder-dashboard__tabs{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:.75rem}.aicoder-dashboard__tabs button{border:1px solid rgba(15,23,42,.2);background:#fff;padding:.45rem .75rem;border-radius:8px;font-weight:600}.aicoder-dashboard__tabs button.is-active{border-color:var(--aicoder-primary);background:#0f4c811a}.aicoder-dashboard__state{padding:.85rem;border-radius:10px;background:#f8fafc}.aicoder-dashboard__state--error{display:flex;justify-content:space-between;align-items:center;gap:.5rem;background:#fff7ed}.aicoder-dashboard__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:.75rem}.tile{padding:.8rem;border:1px solid rgba(15,23,42,.12);border-radius:10px;background:#fff}.tile span{display:block;font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:#0f172a9e}.tile strong{display:block;margin-top:.3rem;font-size:1rem}.aicoder-dashboard__tiers{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:.75rem}.tier{padding:.85rem;border-radius:10px;border:1px solid rgba(15,23,42,.12);background:#f8fafc}.tier--active{border-color:var(--aicoder-primary);background:#0f4c8114}.tier h4{margin:0}.tier p{margin:.45rem 0}.aicoder-dashboard__changes ul{margin:0;padding-left:1.1rem}.aicoder-dashboard__subscription{display:grid;gap:.75rem}.aicoder-dashboard__subscription-actions{display:flex;flex-wrap:wrap;gap:.5rem}.aicoder-dashboard__primary-action{border:1px solid var(--aicoder-primary);background:var(--aicoder-primary);color:#fff;padding:.5rem .85rem;border-radius:8px;font-weight:700}.aicoder-dashboard__primary-action[disabled]{opacity:.55;cursor:not-allowed}.aicoder-dashboard__hint{margin:0;color:#334155;font-size:.88rem}.aicoder-dashboard__hint--warning{color:#9a3412}\n"]})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardComponent,decorators:[{type:Component,args:[{selector:"resolveio-client-lib-aicoder-dashboard",standalone:!1,template:'<div class="aicoder-dashboard">\n\t<div class="aicoder-dashboard__tabs">\n\t\t<button type="button" [class.is-active]="activeTab === \'overview\'" (click)="setTab(\'overview\')">Overview</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'database\'" (click)="setTab(\'database\')">Database</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'infrastructure\'" (click)="setTab(\'infrastructure\')">Infrastructure</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'code_changes\'" (click)="setTab(\'code_changes\')">Code Changes</button>\n\t\t<button type="button" [class.is-active]="activeTab === \'subscription\'" (click)="setTab(\'subscription\')">Subscription</button>\n\t</div>\n\n\t@if (loading) {\n\t\t<div class="aicoder-dashboard__state">Loading dashboard details...</div>\n\t} @else if (errorMessage) {\n\t\t<div class="aicoder-dashboard__state aicoder-dashboard__state--error">\n\t\t\t<span>{{ errorMessage }}</span>\n\t\t\t<button type="button" (click)="requestRetry()">Retry</button>\n\t\t</div>\n\t} @else {\n\t\t@if (activeTab === \'overview\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>App</span><strong>{{ summary.overview.app_name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.overview.app_status }}</strong></div>\n\t\t\t\t<div class="tile"><span>Tier</span><strong>{{ summary.overview.plan_tier }}</strong></div>\n\t\t\t\t<div class="tile"><span>Max users</span><strong>{{ summary.overview.max_users }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'database\') {\n\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t<div class="tile"><span>Name</span><strong>{{ summary.database.name }}</strong></div>\n\t\t\t\t<div class="tile"><span>Host</span><strong>{{ summary.database.host }}</strong></div>\n\t\t\t\t<div class="tile"><span>Status</span><strong>{{ summary.database.status }}</strong></div>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'infrastructure\') {\n\t\t\t<div class="aicoder-dashboard__tiers">\n\t\t\t\t@for (tier of tierOptions; track tier.id) {\n\t\t\t\t\t<div class="tier" [class.tier--active]="isCurrentTier(tier.id)">\n\t\t\t\t\t\t<h4>{{ tier.label }}</h4>\n\t\t\t\t\t\t<p>{{ tier.description }}</p>\n\t\t\t\t\t\t<button type="button" (click)="requestTierChange(tier.id)">Choose {{ tier.label }}</button>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'code_changes\') {\n\t\t\t<div class="aicoder-dashboard__changes">\n\t\t\t\t<p>{{ summary.code_changes.release_notes }}</p>\n\t\t\t\t<ul>\n\t\t\t\t\t@for (item of summary.code_changes.recent_updates; track item) {\n\t\t\t\t\t\t<li>{{ item }}</li>\n\t\t\t\t\t}\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t}\n\t\t@if (activeTab === \'subscription\') {\n\t\t\t<div class="aicoder-dashboard__subscription">\n\t\t\t\t<div class="aicoder-dashboard__grid">\n\t\t\t\t\t<div class="tile"><span>Package</span><strong>{{ summary.subscription?.current_package || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Tier</span><strong>{{ summary.subscription?.current_tier || summary.overview.plan_tier || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Price</span><strong>{{ summary.subscription?.package_price_label || \'N/A\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Billing status</span><strong>{{ summary.subscription?.billing_status || \'Unknown\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Billing mode</span><strong>{{ summary.subscription?.billing_mode || \'Unknown\' }}</strong></div>\n\t\t\t\t\t<div class="tile"><span>Subscription</span><strong>{{ summary.subscription?.subscription_status || \'N/A\' }}</strong></div>\n\t\t\t\t</div>\n\t\t\t\t@if (summary.subscription?.current_period_end) {\n\t\t\t\t\t<p class="aicoder-dashboard__hint">Current period ends: {{ summary.subscription?.current_period_end }}</p>\n\t\t\t\t}\n\t\t\t\t@if (summary.subscription?.downgrade_reason) {\n\t\t\t\t\t<p class="aicoder-dashboard__hint aicoder-dashboard__hint--warning">{{ summary.subscription?.downgrade_reason }}</p>\n\t\t\t\t}\n\t\t\t\t<div class="aicoder-dashboard__subscription-actions">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype="button"\n\t\t\t\t\t\tclass="aicoder-dashboard__primary-action"\n\t\t\t\t\t\t(click)="requestManageSubscription()"\n\t\t\t\t\t\t[disabled]="!hasManageSubscriptionUrl()"\n\t\t\t\t\t>\n\t\t\t\t\t\tUpgrade Or Manage Plan\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t}\n\t}\n</div>\n',styles:[".aicoder-dashboard{--aicoder-primary: #0f4c81;font-family:inherit}.aicoder-dashboard__tabs{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:.75rem}.aicoder-dashboard__tabs button{border:1px solid rgba(15,23,42,.2);background:#fff;padding:.45rem .75rem;border-radius:8px;font-weight:600}.aicoder-dashboard__tabs button.is-active{border-color:var(--aicoder-primary);background:#0f4c811a}.aicoder-dashboard__state{padding:.85rem;border-radius:10px;background:#f8fafc}.aicoder-dashboard__state--error{display:flex;justify-content:space-between;align-items:center;gap:.5rem;background:#fff7ed}.aicoder-dashboard__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:.75rem}.tile{padding:.8rem;border:1px solid rgba(15,23,42,.12);border-radius:10px;background:#fff}.tile span{display:block;font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:#0f172a9e}.tile strong{display:block;margin-top:.3rem;font-size:1rem}.aicoder-dashboard__tiers{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:.75rem}.tier{padding:.85rem;border-radius:10px;border:1px solid rgba(15,23,42,.12);background:#f8fafc}.tier--active{border-color:var(--aicoder-primary);background:#0f4c8114}.tier h4{margin:0}.tier p{margin:.45rem 0}.aicoder-dashboard__changes ul{margin:0;padding-left:1.1rem}.aicoder-dashboard__subscription{display:grid;gap:.75rem}.aicoder-dashboard__subscription-actions{display:flex;flex-wrap:wrap;gap:.5rem}.aicoder-dashboard__primary-action{border:1px solid var(--aicoder-primary);background:var(--aicoder-primary);color:#fff;padding:.5rem .85rem;border-radius:8px;font-weight:700}.aicoder-dashboard__primary-action[disabled]{opacity:.55;cursor:not-allowed}.aicoder-dashboard__hint{margin:0;color:#334155;font-size:.88rem}.aicoder-dashboard__hint--warning{color:#9a3412}\n"]}]}],ctorParameters:()=>[{type:AICoderDashboardService}],propDecorators:{summary:[{type:Input}],loading:[{type:Input}],errorMessage:[{type:Input}],activeTab:[{type:Input}],tierOptions:[{type:Input}],tabChange:[{type:Output}],retry:[{type:Output}],tierChange:[{type:Output}],manageSubscription:[{type:Output}]}});class AICoderWorkbenchApiService{http;tokenManager;sessionHeaderName="X-AI-Coder-Session";appTokenHeaderName="X-AI-Coder-App-Token";sessionRefreshSkewMs=6e4;defaultSessionTtlMs=144e5;sessionByApiBase=new Map;exchangeByApiBase=new Map;constructor(t,e){this.http=t,this.tokenManager=e}get(t,e,r=""){return this.authHeaders(t,r).pipe(switchMap(r=>this.http.get(this.buildUrl(t,e),{headers:r})))}post(t,e,r={},i=""){return this.authHeaders(t,i).pipe(switchMap(i=>this.http.post(this.buildUrl(t,e),r,{headers:i})))}patch(t,e,r={},i=""){return this.authHeaders(t,i).pipe(switchMap(i=>this.http.patch(this.buildUrl(t,e),r,{headers:i})))}delete(t,e,r=""){return this.authHeaders(t,r).pipe(switchMap(r=>this.http.delete(this.buildUrl(t,e),{headers:r})))}resolveDefaultApiBase(){const t=String(window?.location?.hostname||"").trim().toLowerCase();return"aicoder.resolveio.com"===t||t.endsWith(".aicoder.resolveio.com")?"https://backend.aicoder.resolveio.com":""}authHeaders(t,e=""){const r=String(e||"").trim();return r?new Observable(t=>{t.next(new HttpHeaders({[this.appTokenHeaderName]:r})),t.complete()}):this.ensureSessionToken(t).pipe(map(t=>new HttpHeaders({[this.sessionHeaderName]:t})))}ensureSessionToken(t){const e=this.normalizeApiBase(t),r=this.sessionByApiBase.get(e);if(r&&r.token&&r.expiresAtMs>Date.now()+this.sessionRefreshSkewMs)return new Observable(t=>{t.next(r.token),t.complete()});const i=this.exchangeByApiBase.get(e);if(i)return i;const a=String(this.tokenManager.getToken("accessToken")||"").trim();if(!a)return throwError(()=>new Error("Sign in before using AICoder."));const s=new HttpHeaders({Authorization:`Bearer ${a}`}),o=this.http.post(this.buildUrl(e,"/api/ai-coder/auth/session"),{},{headers:s}).pipe(map(t=>{const r=String(t?.token||"").trim();if(!r)throw new Error("AICoder session response did not include a token.");return this.sessionByApiBase.set(e,{token:r,expiresAtMs:this.resolveSessionExpiresAtMs(t)}),r}),tap({error:()=>this.sessionByApiBase.delete(e),complete:()=>this.exchangeByApiBase.delete(e)}),shareReplay(1));return this.exchangeByApiBase.set(e,o),o}resolveSessionExpiresAtMs(t){const e=t?.expires_at?new Date(t.expires_at).getTime():0;if(Number.isFinite(e)&&e>Date.now())return e;const r=1e3*Number(t?.expires_in_seconds||0);return Date.now()+(r>0?r:this.defaultSessionTtlMs)}buildUrl(t,e){const r=this.normalizeApiBase(t),i=String(e||"").trim();return r?`${r}${i.startsWith("/")?i:`/${i}`}`:i}normalizeApiBase(t){return String(t||"").trim().replace(/\/+$/,"")}static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchApiService,deps:[{token:i1.HttpClient},{token:i2.TokenManagerService}],target:i0.ɵɵFactoryTarget.Injectable});static"ɵprov"=i0.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchApiService,providedIn:"root"})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchApiService,decorators:[{type:Injectable,args:[{providedIn:"root"}]}],ctorParameters:()=>[{type:i1.HttpClient},{type:i2.TokenManagerService}]});class AICoderWorkbenchComponent{api;appId="";appName="AICoder App";apiBase="";appToken="";defaultTab="chats";compact=!1;showHeader=!0;autoRefresh=!0;activeTab="chats";jobs=[];selectedJob=null;jobLogs=[];qaEvidence=[];deployOperations=[];deployJobs=[];gitStatus=null;isLoading=!1;isLoadingQaEvidence=!1;isSubmitting=!1;errorMessage="";successMessage="";requestTitle="";requestMessage="";chatMessage="";workflowMode="implementation";requestKind="bug_or_feature";dataChangePolicy="review_before_write";deploymentTarget="branch_pr";evidenceNotes="";qaFocus="";riskNotes="";refreshSub;constructor(t){this.api=t}ngOnInit(){this.activeTab=this.normalizeTab(this.defaultTab),this.apiBase||(this.apiBase=this.api.resolveDefaultApiBase()),this.refresh(),this.startAutoRefresh()}ngOnChanges(t){t.defaultTab&&this.defaultTab&&(this.activeTab=this.normalizeTab(this.defaultTab)),(t.appId&&!t.appId.firstChange||t.apiBase&&!t.apiBase.firstChange)&&this.refresh()}ngOnDestroy(){this.refreshSub?.unsubscribe()}setTab(t){this.activeTab=this.normalizeTab(t),"git"!==t||this.gitStatus||this.loadGitStatus(),"deploy"!==t||this.deployOperations.length||this.loadDeployHistory()}refresh(){this.normalizedAppId?(this.loadJobs(),this.loadDeployHistory(),this.loadGitStatus()):this.errorMessage="AICoder app id is missing."}selectJob(t){this.selectedJob=t,this.loadJobLogs(t._id),this.loadJobQaEvidence(t._id)}submitRequest(){if(!this.normalizedAppId||this.isSubmitting)return;const t=String(this.requestMessage||"").trim();if(!t)return void(this.errorMessage="Describe what you want changed before starting the chat.");this.isSubmitting=!0,this.clearMessages();const e=this.isReadOnlyQuestion(t),r=e?`/api/ai-coder/apps/${this.normalizedAppId}/questions`:`/api/ai-coder/apps/${this.normalizedAppId}/requests`;this.api.post(this.apiBase,r,{title:String(this.requestTitle||"").trim(),message:t,workflow_mode:this.workflowMode,request_kind:e?"question":this.requestKind,data_change_policy:this.dataChangePolicy,deployment_target:this.deploymentTarget,evidence_notes:this.evidenceNotes,qa_focus:this.qaFocus,risk_notes:this.riskNotes},this.normalizedAppToken).subscribe({next:t=>{if(this.successMessage=e?"AI answered your question.":"AI chat started.",this.requestTitle="",this.requestMessage="",t?.job){const e=this.normalizeJob(t.job);this.jobs=[e,...this.jobs.filter(t=>t._id!==e._id)],this.selectJob(e)}},error:t=>this.handleError(t,"Could not start the AI chat."),complete:()=>this.isSubmitting=!1})}sendChatMessage(){const t=this.selectedJobId,e=String(this.chatMessage||"").trim();if(!t||!e||this.isSubmitting)return;this.isSubmitting=!0,this.clearMessages();const r=this.selectedJobIsQuestionOnly?`/api/ai-coder/jobs/${t}/question`:`/api/ai-coder/jobs/${t}/prompt`;this.api.post(this.apiBase,r,{message:e},this.normalizedAppToken).subscribe({next:t=>{this.successMessage=this.selectedJobIsQuestionOnly?"AI answered your question.":"Message sent to AICoder.",this.chatMessage="",t?.job&&(this.mergeJob(t.job),this.selectJob(t.job))},error:t=>this.handleError(t,"Could not send the message."),complete:()=>this.isSubmitting=!1})}approveSelectedJob(){this.runJobAction("approve","Approved.")}createPullRequest(){this.runJobAction("create-pr","Test review started.")}publishSelectedJob(){this.runJobAction("publish","Approved for live deploy.")}sendToTestSite(){this.canSendSelectedJobToTestSite&&this.runJobAction("test-deploy","Test site is ready.")}approveAndDeployLive(){this.canApproveSelectedJobLive&&window.confirm("Put this tested version live now? The temporary test site will be removed after this starts.")&&this.runJobAction("publish","This version is being put live.")}deleteSelectedChat(){const t=this.selectedJobId;t&&!this.isSubmitting&&window.confirm("Delete this AI chat? Any temporary test site for this chat will be removed too.")&&(this.isSubmitting=!0,this.clearMessages(),this.api.delete(this.apiBase,`/api/ai-coder/jobs/${t}`,this.normalizedAppToken).subscribe({next:()=>{this.successMessage="AI chat deleted.",this.jobs=this.jobs.filter(e=>e._id!==t),this.selectedJob=this.jobs[0]||null,this.jobLogs=[],this.qaEvidence=[],this.selectedJob&&(this.loadJobLogs(this.selectedJob._id),this.loadJobQaEvidence(this.selectedJob._id))},error:t=>this.handleError(t,"Could not delete the AI chat."),complete:()=>this.isSubmitting=!1}))}queueDeploy(t){this.normalizedAppId&&!this.isSubmitting&&(this.isSubmitting=!0,this.clearMessages(),this.api.post(this.apiBase,`/api/ai-coder/apps/${this.normalizedAppId}/deploy`,{deploy_client:"full"===t||"client"===t,deploy_backend:"full"===t||"backend"===t,clear_destination:!0,invalidate:!0},this.normalizedAppToken).subscribe({next:()=>{this.successMessage="full"===t?"Full deploy queued.":("client"===t?"Client":"Server")+" deploy queued.",this.loadDeployHistory()},error:t=>this.handleError(t,"Could not queue deploy."),complete:()=>this.isSubmitting=!1}))}openPr(t=this.selectedJob){const e=this.resolvePrUrl(t);e&&window.open(e,"_blank","noopener")}openTestSite(){const t=this.selectedJobTestSiteUrl;t&&window.open(t,"_blank","noopener")}loadJobs(){this.normalizedAppId&&(this.isLoading=!0,this.api.get(this.apiBase,`/api/ai-coder/apps/${this.normalizedAppId}/jobs`,this.normalizedAppToken).subscribe({next:t=>{const e=this.selectedJobId;this.jobs=(t?.jobs||[]).map(t=>this.normalizeJob(t));const r=e&&this.jobs.find(t=>t._id===e)||null;r?(this.selectedJob=r,this.loadJobLogs(r._id),this.loadJobQaEvidence(r._id)):!this.selectedJob&&this.visibleJobs.length&&this.selectJob(this.visibleJobs[0])},error:t=>this.handleError(t,"Could not load AI chats."),complete:()=>this.isLoading=!1}))}loadJobLogs(t){const e=String(t||"").trim();e?this.api.get(this.apiBase,`/api/ai-coder/jobs/${e}/logs?limit=80`,this.normalizedAppToken).subscribe({next:t=>this.jobLogs=this.buildTimeline(this.selectedJob,t?.logs||[]),error:t=>this.handleError(t,"Could not load chat history.")}):this.jobLogs=[]}loadJobQaEvidence(t){const e=String(t||"").trim();e?(this.isLoadingQaEvidence=!0,this.api.get(this.apiBase,`/api/ai-coder/jobs/${e}/qa-evidence`,this.normalizedAppToken).subscribe({next:t=>this.qaEvidence=(t?.evidence||[]).filter(t=>this.resolveQaEvidenceUrl(t)),error:()=>{this.qaEvidence=[],this.isLoadingQaEvidence=!1},complete:()=>this.isLoadingQaEvidence=!1})):this.qaEvidence=[]}loadDeployHistory(){this.normalizedAppId&&this.api.get(this.apiBase,`/api/ai-coder/apps/${this.normalizedAppId}/deploy/history`,this.normalizedAppToken).subscribe({next:t=>{this.deployOperations=t?.operations||[],this.deployJobs=t?.jobs||t?.deploy_jobs||[]},error:t=>this.handleError(t,"Could not load deploy history.")})}loadGitStatus(){this.normalizedAppId&&this.api.get(this.apiBase,`/api/ai-coder/apps/${this.normalizedAppId}/git`,this.normalizedAppToken).subscribe({next:t=>this.gitStatus=t?.status||t||null,error:t=>this.handleError(t,"Could not load git status.")})}get normalizedAppId(){return String(this.appId||"").trim()}get selectedJobId(){return String(this.selectedJob?._id||"").trim()}get normalizedAppToken(){return String(this.appToken||"").trim()}get activeDeploys(){return this.deployOperations.filter(t=>"In Progress"===String(t?.status||"").trim())}get recentDeploys(){return this.deployOperations.filter(t=>"In Progress"!==String(t?.status||"").trim()).slice(0,6)}get visibleJobs(){return this.computeVisibleJobs(this.jobs)}get hasSelectedJob(){return!!this.selectedJob}get selectedJobIsWorking(){return this.isActiveJob(this.selectedJob)}get selectedJobIsQuestionOnly(){return this.isQuestionOnlyJob(this.selectedJob)}get selectedJobHasChanges(){return this.hasJobChanges(this.selectedJob)}get selectedJobHasReview(){return!!this.resolvePrUrl(this.selectedJob)}get selectedJobTestSiteUrl(){return this.resolveTestSiteUrl(this.selectedJob)}get canSendSelectedJobToTestSite(){return!!this.selectedJobId&&!this.isSubmitting&&!this.selectedJobIsWorking&&this.selectedJobHasChanges&&!this.selectedJobTestSiteUrl}get canApproveSelectedJobLive(){return!!this.selectedJobId&&!this.isSubmitting&&!this.selectedJobIsWorking&&this.selectedJobHasChanges&&(!!this.selectedJobTestSiteUrl||this.selectedJobHasReview)}get selectedJobNextStepMessage(){return this.selectedJob?this.selectedJobIsQuestionOnly?"":this.selectedJobIsWorking?"I am working on this. I will update this chat when there is something ready for you to review.":this.selectedJobTestSiteUrl?"Your test site is ready. Open it and check the change before putting it live.":this.selectedJobHasReview?"A test version is ready. Open it first, then approve it when everything looks right.":this.selectedJobHasChanges?"The update is ready. Send it to the test site when you are ready.":this.isFailedJob(this.selectedJob)?"This needs your attention before it can continue. Add a reply with what you want to try next.":"This chat will update when there is something new.":""}isActiveJob(t){if(this.isQuestionOnlyJob(t)&&"COMPLETE"===String(t?.phase||"").toUpperCase())return!1;const e=String(t?.status||"").toLowerCase(),r=String(t?.phase||t?.stage||"").toLowerCase();return!(e.includes("fail")||e.includes("error")||e.includes("complete")||e.includes("passed"))&&(!r.includes("complete")&&!r.includes("review")&&("queued"===e||"in progress"===e||"running"===e||"pending"===e||["discovery","planning","execution","linting","building"].includes(r)))}resolvePrUrl(t){return String(t?.pr_url||t?.pull_request_url||"").trim()}resolveTestSiteUrl(t){return String(t?.testSiteUrl||t?.test_website_url||t?.test_url||"").trim()}friendlyJobStatus(t){if(this.isQuestionOnlyJob(t))return this.isActiveJob(t)?"AI is answering":"Answered";const e=String(t?.status||t?.stage||"").trim().toLowerCase(),r=String(t?.phase||"").trim().toLowerCase();return e||r?e.includes("complete")||e.includes("passed")||r.includes("complete")?this.resolveTestSiteUrl(t)?"Test site ready":this.resolvePrUrl(t)?"Ready to test":"Ready to review":e.includes("fail")||e.includes("error")?"Needs attention":e.includes("progress")||e.includes("running")||e.includes("pending")||e.includes("queued")?"AI is working":e.includes("pause")||e.includes("stopped")?"Paused":this.toTitle(e||r):"Waiting to start"}friendlyDeployStatus(t){const e=String(t?.status||"").trim(),r=String(t?.type||"").trim();if(!e&&!r)return"Website update";return`${r?this.toTitle(r.replace(/ai coder/gi,"").replace(/backend/gi,"server")):"Website update"}${e?` - ${e}`:""}`}formatDate(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);return Number.isNaN(e.getTime())?String(t):e.toLocaleString()}resolveQaEvidenceUrl(t){return String(t?.url||t?.data_url||"").trim()}buildTimeline(t,e){const r=[],i=this.isQuestionOnlyJob(t),a=t=>{const e=this.sanitizeUserFacingMessage(t.message);if(!e)return;const i=`${t.role}:${t.label}:${t.date||""}:${e}`;r.some(t=>t.id===i||t.message===e)||r.push({...t,id:i,message:e})};(Array.isArray(t?.conversation)&&t?.conversation||[]).forEach((e,r)=>{const s="assistant"===String(e?.role||"").toLowerCase()?"assistant":"user",o="user"===s?this.extractUserRequest(e?.message||""):String(e?.message||"");a({role:s,label:"user"===s?"You":"Assistant",message:o,date:this.formatDate(e?.timestamp||e?.createdAt),active:!1}),0===r&&"user"===s&&this.isActiveJob(t)&&a({role:"assistant",label:"Assistant",message:i?"I am checking this and will answer without changing anything.":"I am working on this. I will update this chat when there is something ready for you to review.",date:"",active:!0})});const s=this.extractQuestionAnswerFromSummary(t);return s&&a({role:"assistant",label:"Assistant",message:s,date:this.formatDate(t?.updatedAt),active:!1}),!r.length&&this.isActiveJob(t)&&a({role:"assistant",label:"Assistant",message:i?"I am reading this and will answer shortly.":"I am working on this. I will update this chat when there is something ready for you to review.",date:"",active:!0}),r}extractUserRequest(t){const e=String(t||"").trim(),r=e.match(/(?:^|\n)Request:\s*([\s\S]*?)(?:\n\n|(?:^|\n)AICoder case runner contract:|$)/i);return r?.[1]?r[1].trim():e.split("\n").filter(t=>!t.match(/^(This is a change request|App:|Template:|App root:|AICoder|Case-building|Data safety|QA playbook|Branch, PR|Constraints:|- )/i)).join("\n").trim()}sanitizeUserFacingMessage(t){const e=String(t||"").replace(/```[\s\S]*?```/g,"[details hidden]").replace(/^\s*\[[^\]]+\]\s*/,"").replace(/\/var\/ai-workspace\/[^\s)]+/g,"the app workspace").replace(/ResolveIO\/aicoder-[\w-]+/g,"the app repository").replace(/apps\/[a-f0-9]{24}/gi,"the app").replace(/[a-f0-9]{24}/gi,"the app").replace(/\[([^\]]+)\]\([^)]+\)/g,(t,e)=>this.isInternalText(e)?"the app":e).replace(/\[([^\]]+)\]/g,(t,e)=>this.isInternalText(e)?"":e).trim();if(!e||this.isInternalOnlyMessage(e))return"";let r=!1;return e.split(/\r?\n/).map(t=>this.cleanUserFacingLine(t)).filter(t=>{const e=t.trim();return!!e&&(!this.isInternalLine(e)||!(!/^verification\s*:/i.test(e)||r)&&(r=!0,!0))}).map(t=>/^verification\s*:/i.test(t)?"I checked the update after making the change.":t).join("\n").replace(/\n{3,}/g,"\n\n").trim()}cleanUserFacingLine(t){return String(t||"").replace(/^\s*[-*]\s+/,"").replace(/^\[[^\]]+\]\s*/,"").replace(/^(targeted ui maintenance change|maintenance change|implementation update|change summary|summary)\s*/i,"").replace(/`([^`]+)`/g,(t,e)=>this.isInternalText(e)?"the app":e).replace(/\btop-nav\b/gi,"top navigation").replace(/\s+from the app\b[\s\S]*$/i,".").replace(/\s+/g," ").trim()}isInternalOnlyMessage(t){const e=String(t||"").toLowerCase();return e.includes("deterministic review")||e.includes("change request final review")||e.includes("cycle 1 summary")||e.includes("gates ")||e.includes("lint:pass")||e.includes("build:pass")||e.includes("review:pass")||e.includes("validate:pass")||e.includes("modified files captured")}isInternalLine(t){const e=String(t||"").toLowerCase();return/^verification\s*:/i.test(t)||this.isInternalText(t)||e.includes("grep")||e.includes("npm ")||e.includes("lint")||e.includes("build-dev")||e.includes("build-prod")||e.includes("workspace")||e.includes("routerlink")||e.includes("navtabs")||e.includes("fixtures")||e.includes("migrations")||e.includes("schemas")||e.includes("server flows")||e.includes("package files")||e.includes("deterministic review")||e.includes("cycle ")||e.includes("gates ")}isInternalText(t){const e=String(t||"").trim();return!!e&&(/(^|[\s"'(])(?:\.?\/)?[\w.-]+\/[\w./-]+/.test(e)||/\b[\w.-]+\.(ts|tsx|js|jsx|html|scss|css|json|md|yml|yaml|lock)\b/i.test(e)||/\b(package\.json|package-lock\.json|node_modules|app workspace|app repository|git|github|branch|pull request|pr_url)\b/i.test(e)||/\b(npm|grep|routerlink|navtabs|typescript|angular|nodejs|codebuild|s3:\/\/)\b/i.test(e))}computeVisibleJobs(t){const e=new Set,r=[];return(t||[]).forEach(t=>{const i=this.jobDisplayKey(t);e.has(i)||(e.add(i),r.push(t))}),r}jobDisplayKey(t){const e=(Array.isArray(t?.conversation)?t.conversation:[]).find(t=>"assistant"!==String(t?.role||"").toLowerCase())?.message||"";return String(`${t?.title||""} ${e||t?.description||""}`||t?._id||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim().slice(0,180)}hasJobChanges(t){return!this.isQuestionOnlyJob(t)&&this.hasJobChangesWithoutQuestionGuard(t)}hasJobChangesWithoutQuestionGuard(t){const e=t?.artifacts?.modifiedFiles||{},r=t?.artifacts?.diffs||{};return Object.keys(e).length>0||Object.keys(r).length>0}isQuestionOnlyJob(t){return!0===t?.runPolicy?.questionOnly||String(t?.responseSummary||"").includes("AICODER_QUESTION_ONLY")||this.isLikelyQuestionChat(t)}isLikelyQuestionChat(t){if(!t||this.hasJobChangesWithoutQuestionGuard(t)||this.resolvePrUrl(t))return!1;const e=(Array.isArray(t.conversation)?t.conversation:[]).find(t=>"assistant"!==String(t?.role||"").toLowerCase())?.message||"";return this.isReadOnlyQuestion(String(e||t.description||t.title||""))}extractQuestionAnswerFromSummary(t){const e=String(t?.responseSummary||"").trim();return e.includes("AICODER_QUESTION_ONLY")?e.replace(/^AICODER_QUESTION_ONLY\s*/i,"").trim():""}isReadOnlyQuestion(t){const e=String(t||"").trim().toLowerCase();if(!e)return!1;if(/\b(fix|change|update|add|remove|delete|create|build|make|implement|deploy|merge|push|edit|replace|rename|turn on|turn off|enable|disable|set|move)\b/i.test(e))return!1;return e.includes("?")||/(^|\b)(what|why|how|where|when|who|which|explain|describe|tell me|walk me through|show me|does|do|is|are|can i|can you)\b/i.test(e)}isFailedJob(t){const e=String(t?.status||t?.phase||"").toLowerCase();return e.includes("fail")||e.includes("error")||e.includes("blocked")}runJobAction(t,e){const r=this.selectedJobId;r&&!this.isSubmitting&&(this.isSubmitting=!0,this.clearMessages(),this.api.post(this.apiBase,`/api/ai-coder/jobs/${r}/${t}`,{},this.normalizedAppToken).subscribe({next:t=>{this.successMessage=e,t?.job&&(this.mergeJob(t.job),this.selectJob(t.job))},error:e=>this.handleError(e,`Could not ${t.replace("-"," ")}.`),complete:()=>this.isSubmitting=!1}))}mergeJob(t){const e=this.normalizeJob(t);this.jobs=[e,...this.jobs.filter(t=>t._id!==e._id)],this.selectedJob=e}normalizeJob(t){return{...t,_id:String(t?._id||"").trim(),title:String(t?.title||"AI Chat").trim(),status:String(t?.status||"").trim()||"Queued"}}normalizeTab(t){return"deploy"===t||"git"===t?t:"chats"}toTitle(t){return String(t||"").replace(/[_-]+/g," ").replace(/\s+/g," ").trim().split(" ").filter(Boolean).map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")||"Waiting to start"}startAutoRefresh(){this.refreshSub?.unsubscribe(),this.autoRefresh&&(this.refreshSub=interval(15e3).subscribe(()=>{(this.jobs.some(t=>this.isActiveJob(t))||this.activeDeploys.length||this.selectedJobTestSiteUrl&&!this.qaEvidence.length)&&this.refresh()}))}clearMessages(){this.errorMessage="",this.successMessage=""}handleError(t,e){this.errorMessage=String(t?.error?.message||t?.message||e),this.successMessage="",this.isSubmitting=!1,this.isLoading=!1}static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchComponent,deps:[{token:AICoderWorkbenchApiService}],target:i0.ɵɵFactoryTarget.Component});static"ɵcmp"=i0.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"21.1.2",type:AICoderWorkbenchComponent,isStandalone:!1,selector:"resolveio-client-lib-aicoder-workbench",inputs:{appId:"appId",appName:"appName",apiBase:"apiBase",appToken:"appToken",defaultTab:"defaultTab",compact:"compact",showHeader:"showHeader",autoRefresh:"autoRefresh"},usesOnChanges:!0,ngImport:i0,template:'<section class="aicoder-workbench" [class.aicoder-workbench--compact]="compact">\n\t@if (showHeader) {\n\t\t<header class="aicoder-workbench__header">\n\t\t\t<div>\n\t\t\t\t<p>AI Chats</p>\n\t\t\t\t<h2>{{ appName || \'AICoder App\' }}</h2>\n\t\t\t</div>\n\t\t\t<button type="button" class="aicoder-workbench__ghost" (click)="refresh()" [disabled]="isLoading || isSubmitting">Refresh</button>\n\t\t</header>\n\t}\n\n\t@if (errorMessage) {\n\t\t<div class="aicoder-workbench__notice aicoder-workbench__notice--error">{{ errorMessage }}</div>\n\t}\n\t@if (successMessage) {\n\t\t<div class="aicoder-workbench__notice aicoder-workbench__notice--success">{{ successMessage }}</div>\n\t}\n\n\t<div class="aicoder-workbench__intro">\n\t\t<div>\n\t\t\t<p class="aicoder-workbench__eyebrow">AI Assistant</p>\n\t\t\t<h3>Ask for help or tell us what to change.</h3>\n\t\t\t<p>Ask questions in plain English, or request updates. AI will answer, guide you, or prepare changes when there is work to do.</p>\n\t\t</div>\n\t\t<button type="button" class="aicoder-workbench__ghost" (click)="refresh()" [disabled]="isLoading || isSubmitting">Refresh Chats</button>\n\t</div>\n\n\t<div class="aicoder-workbench__layout">\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--compose">\n\t\t\t<h3>New AI Chat</h3>\n\t\t\t<input class="aicoder-workbench__input" [(ngModel)]="requestTitle" placeholder="Short name for this chat" />\n\t\t\t<textarea class="aicoder-workbench__textarea" rows="6" [(ngModel)]="requestMessage" placeholder="Ask a question, explain what is confusing, or describe what you want changed."></textarea>\n\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="submitRequest()" [disabled]="isSubmitting || !requestMessage.trim()">\n\t\t\t\tStart AI Chat\n\t\t\t</button>\n\t\t</section>\n\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--list">\n\t\t\t<div class="aicoder-workbench__panel-head">\n\t\t\t\t<h3>Your AI Chats</h3>\n\t\t\t\t<span>{{ visibleJobs.length }}</span>\n\t\t\t</div>\n\t\t\t@if (!jobs.length && !isLoading) {\n\t\t\t\t<p class="aicoder-workbench__empty">No AI chats yet.</p>\n\t\t\t}\n\t\t\t@if (isLoading && !jobs.length) {\n\t\t\t\t<p class="aicoder-workbench__empty">Loading AI chats...</p>\n\t\t\t}\n\t\t\t<div class="aicoder-workbench__chat-list">\n\t\t\t\t@for (job of visibleJobs; track job._id) {\n\t\t\t\t\t<button type="button" class="aicoder-workbench__chat" [class.is-active]="selectedJob?._id === job._id" (click)="selectJob(job)">\n\t\t\t\t\t\t<strong>{{ job.title || \'AI Chat\' }}</strong>\n\t\t\t\t\t\t<span>{{ friendlyJobStatus(job) }}</span>\n\t\t\t\t\t\t<small>{{ formatDate(job.updatedAt || job.createdAt) }}</small>\n\t\t\t\t\t</button>\n\t\t\t\t}\n\t\t\t</div>\n\t\t</section>\n\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--detail">\n\t\t\t@if (selectedJob) {\n\t\t\t\t<div class="aicoder-workbench__detail-head">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<p class="aicoder-workbench__eyebrow">Open Chat</p>\n\t\t\t\t\t\t<h3>{{ selectedJob.title || \'AI Chat\' }}</h3>\n\t\t\t\t\t\t<span class="aicoder-workbench__status">{{ friendlyJobStatus(selectedJob) }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<button type="button" class="aicoder-workbench__danger" (click)="deleteSelectedChat()" [disabled]="isSubmitting">Delete Chat</button>\n\t\t\t\t</div>\n\n\t\t\t\t@if (selectedJobTestSiteUrl || canSendSelectedJobToTestSite || canApproveSelectedJobLive || selectedJobHasReview) {\n\t\t\t\t\t<div class="aicoder-workbench__actions" aria-label="AI chat actions">\n\t\t\t\t\t\t@if (selectedJobTestSiteUrl) {\n\t\t\t\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="openTestSite()" [disabled]="isSubmitting">\n\t\t\t\t\t\t\t\tOpen Test Site\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (canSendSelectedJobToTestSite) {\n\t\t\t\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="sendToTestSite()" [disabled]="isSubmitting">\n\t\t\t\t\t\t\t\tSend to Test Site\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (canApproveSelectedJobLive) {\n\t\t\t\t\t\t\t<button type="button" (click)="approveAndDeployLive()" [disabled]="isSubmitting">\n\t\t\t\t\t\t\t\tPut This Version Live\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (selectedJobHasReview) {\n\t\t\t\t\t\t\t<button type="button" (click)="openPr()" [disabled]="!resolvePrUrl(selectedJob)">\n\t\t\t\t\t\t\t\tOpen Review\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t\t@else if (selectedJobNextStepMessage) {\n\t\t\t\t\t<div class="aicoder-workbench__next-step">\n\t\t\t\t\t\t@if (selectedJobIsWorking) {\n\t\t\t\t\t\t\t<span class="aicoder-workbench__spinner" aria-hidden="true"></span>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t<p>{{ selectedJobNextStepMessage }}</p>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\n\t\t\t\t@if (qaEvidence.length || selectedJobTestSiteUrl || isLoadingQaEvidence) {\n\t\t\t\t\t<section class="aicoder-workbench__qa">\n\t\t\t\t\t\t<div class="aicoder-workbench__qa-head">\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<p class="aicoder-workbench__eyebrow">QA Pictures</p>\n\t\t\t\t\t\t\t\t<h4>What changed on the test site</h4>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t@if (isLoadingQaEvidence) {\n\t\t\t\t\t\t\t\t<span>Checking...</span>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t@if (qaEvidence.length) {\n\t\t\t\t\t\t\t<div class="aicoder-workbench__qa-grid">\n\t\t\t\t\t\t\t\t@for (image of qaEvidence; track image.id) {\n\t\t\t\t\t\t\t\t\t<a class="aicoder-workbench__qa-card" [href]="resolveQaEvidenceUrl(image)" target="_blank" rel="noopener">\n\t\t\t\t\t\t\t\t\t\t<img [src]="resolveQaEvidenceUrl(image)" [alt]="image.title || \'QA picture\'" loading="lazy" />\n\t\t\t\t\t\t\t\t\t\t<span>{{ image.title || \'QA picture\' }}</span>\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@else {\n\t\t\t\t\t\t\t<p class="aicoder-workbench__qa-empty">QA pictures will appear here after the test site finishes its visual checks.</p>\n\t\t\t\t\t\t}\n\t\t\t\t\t</section>\n\t\t\t\t}\n\n\t\t\t\t<div class="aicoder-workbench__history">\n\t\t\t\t\t@if (!jobLogs.length) {\n\t\t\t\t\t\t<div class="aicoder-workbench__thinking">\n\t\t\t\t\t\t\t<span class="aicoder-workbench__spinner" aria-hidden="true"></span>\n\t\t\t\t\t\t\t<p>AI is getting started.</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t\t@for (log of jobLogs; track log.id) {\n\t\t\t\t\t\t<div class="aicoder-workbench__message" [class.aicoder-workbench__message--user]="log.role === \'user\'" [class.aicoder-workbench__message--assistant]="log.role === \'assistant\'" [class.aicoder-workbench__message--system]="log.role === \'system\'" [class.is-active]="log.active">\n\t\t\t\t\t\t\t<div class="aicoder-workbench__message-meta">\n\t\t\t\t\t\t\t\t<strong>{{ log.label }}</strong>\n\t\t\t\t\t\t\t\t@if (log.date) {\n\t\t\t\t\t\t\t\t\t<span>{{ log.date }}</span>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<p>{{ log.message }}</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\n\t\t\t\t<label class="aicoder-workbench__reply">\n\t\t\t\t\t<span>Reply to AI</span>\n\t\t\t\t\t<textarea class="aicoder-workbench__textarea" rows="4" [(ngModel)]="chatMessage" [placeholder]="selectedJobIsQuestionOnly ? \'Ask a follow-up question.\' : \'Add more details, ask for another change, or answer a question from AI.\'"></textarea>\n\t\t\t\t</label>\n\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="sendChatMessage()" [disabled]="!selectedJobId || !chatMessage.trim() || isSubmitting">\n\t\t\t\t\tSend Reply\n\t\t\t\t</button>\n\t\t\t}\n\t\t\t@else {\n\t\t\t\t<div class="aicoder-workbench__blank">\n\t\t\t\t\t<h3>Select an AI chat</h3>\n\t\t\t\t\t<p>Open a chat from the list or start a new one.</p>\n\t\t\t\t</div>\n\t\t\t}\n\t\t</section>\n\t</div>\n\n\t@if (activeDeploys.length || recentDeploys.length) {\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--updates">\n\t\t\t<div class="aicoder-workbench__panel-head">\n\t\t\t\t<h3>Website Updates</h3>\n\t\t\t\t<span>{{ activeDeploys.length ? \'Running now\' : \'Recent\' }}</span>\n\t\t\t</div>\n\t\t\t<div class="aicoder-workbench__updates">\n\t\t\t\t@for (op of activeDeploys; track op.id || op.jobId) {\n\t\t\t\t\t<div class="aicoder-workbench__update">\n\t\t\t\t\t\t<strong>{{ friendlyDeployStatus(op) }}</strong>\n\t\t\t\t\t\t<span>{{ op.backend_deploy_step || op.aws_build_current_phase || op.message || \'Working on the update\' }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t\t@for (op of recentDeploys; track op.id || op.jobId) {\n\t\t\t\t\t<div class="aicoder-workbench__update">\n\t\t\t\t\t\t<strong>{{ friendlyDeployStatus(op) }}</strong>\n\t\t\t\t\t\t<span>{{ op.message || op.backend_deploy_step || \'Update finished\' }}</span>\n\t\t\t\t\t\t<small>{{ formatDate(op.updatedAt || op.createdAt) }}</small>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t</section>\n\t}\n</section>\n',styles:['.aicoder-workbench{--aicoder-accent: #2f7d73;--aicoder-accent-dark: #155e57;--aicoder-border: #d8e2ec;--aicoder-ink: #1f2933;--aicoder-muted: #64748b;--aicoder-soft: #f5f8fb;display:flex;flex-direction:column;gap:1rem;color:var(--aicoder-ink)}.aicoder-workbench__header,.aicoder-workbench__intro,.aicoder-workbench__panel-head,.aicoder-workbench__detail-head,.aicoder-workbench__actions{display:flex;align-items:center;justify-content:space-between;gap:1rem}.aicoder-workbench__header p,.aicoder-workbench__eyebrow{margin:0 0 .25rem;color:var(--aicoder-accent-dark);font-size:.75rem;font-weight:800;letter-spacing:0;text-transform:uppercase}.aicoder-workbench__header h2,.aicoder-workbench__intro h3,.aicoder-workbench__panel h3{margin:0;font-weight:800;line-height:1.2}.aicoder-workbench__header h2{font-size:1.35rem}.aicoder-workbench__intro{border:1px solid var(--aicoder-border);border-radius:8px;background:#fff;padding:1rem}.aicoder-workbench__intro h3{font-size:1.25rem}.aicoder-workbench__intro p:not(.aicoder-workbench__eyebrow),.aicoder-workbench__blank p,.aicoder-workbench__empty{margin:.35rem 0 0;color:var(--aicoder-muted);line-height:1.45}.aicoder-workbench__layout{display:grid;grid-template-columns:minmax(260px,.75fr) minmax(320px,1.35fr);grid-template-areas:"compose detail" "list detail";gap:1rem;align-items:start}.aicoder-workbench__panel{min-width:0;border:1px solid var(--aicoder-border);border-radius:8px;background:#fff;padding:1rem}.aicoder-workbench__panel--compose{grid-area:compose}.aicoder-workbench__panel--list{grid-area:list}.aicoder-workbench__panel--detail{grid-area:detail}.aicoder-workbench__panel--updates{display:grid;gap:.75rem}.aicoder-workbench__panel-head span,.aicoder-workbench__status{display:inline-flex;align-items:center;border-radius:999px;background:#e7f4f0;color:var(--aicoder-accent-dark);font-size:.78rem;font-weight:800;padding:.25rem .6rem}.aicoder-workbench__next-step,.aicoder-workbench__thinking{display:flex;align-items:center;gap:.65rem;border:1px solid #dbe7ef;border-radius:8px;background:#f7fafc;color:var(--aicoder-muted);margin-top:1rem;padding:.85rem}.aicoder-workbench__next-step p,.aicoder-workbench__thinking p{margin:0;line-height:1.4}.aicoder-workbench__spinner{display:inline-block;width:1rem;height:1rem;border:2px solid #bfd2df;border-top-color:var(--aicoder-accent);border-radius:999px;flex:0 0 auto;animation:aicoder-workbench-spin .85s linear infinite}.aicoder-workbench__input,.aicoder-workbench__textarea{width:100%;border:1px solid #cbd5e1;border-radius:6px;background:#fff;color:var(--aicoder-ink);padding:.7rem .8rem}.aicoder-workbench__textarea{resize:vertical}.aicoder-workbench__input,.aicoder-workbench__textarea{margin:.75rem 0}.aicoder-workbench__ghost,.aicoder-workbench__primary,.aicoder-workbench__danger,.aicoder-workbench__actions button{border:1px solid #cbd5e1;border-radius:6px;background:#fff;color:var(--aicoder-ink);cursor:pointer;font:inherit;font-weight:800;min-height:40px;padding:.6rem .85rem;text-decoration:none}.aicoder-workbench__primary{border-color:var(--aicoder-accent);background:var(--aicoder-accent);color:#fff}.aicoder-workbench__danger{border-color:#fecaca;color:#991b1b}.aicoder-workbench button:disabled{cursor:not-allowed;opacity:.55}.aicoder-workbench__chat-list,.aicoder-workbench__history,.aicoder-workbench__updates{display:flex;flex-direction:column;gap:.5rem}.aicoder-workbench__chat-list,.aicoder-workbench__history{max-height:520px;overflow:auto}.aicoder-workbench__chat,.aicoder-workbench__message,.aicoder-workbench__update{display:flex;flex-direction:column;align-items:flex-start;gap:.25rem;width:100%;border:1px solid #e2e8f0;border-radius:7px;background:var(--aicoder-soft);padding:.8rem;text-align:left}.aicoder-workbench__message{background:#fff}.aicoder-workbench__message--user{border-color:#c9d8e6;background:#f8fafc}.aicoder-workbench__message--assistant{border-color:#b7ded3;background:#f4fbf9}.aicoder-workbench__message--system{background:#fbfdff}.aicoder-workbench__message.is-active{border-color:var(--aicoder-accent)}.aicoder-workbench__message-meta{display:flex;align-items:center;justify-content:space-between;gap:.75rem;width:100%;color:var(--aicoder-muted);font-size:.8rem;line-height:1.35}.aicoder-workbench__message-meta strong{color:var(--aicoder-ink);font-size:.82rem}.aicoder-workbench__chat{cursor:pointer}.aicoder-workbench__chat.is-active{border-color:var(--aicoder-accent);background:#ecfdf5}.aicoder-workbench__chat span,.aicoder-workbench__chat small,.aicoder-workbench__message span,.aicoder-workbench__update span,.aicoder-workbench__update small{color:var(--aicoder-muted);font-size:.82rem;line-height:1.35}.aicoder-workbench__message p{margin:0;white-space:pre-wrap;overflow-wrap:anywhere}.aicoder-workbench__history{min-height:260px;margin:.75rem 0}.aicoder-workbench__qa{border:1px solid #dbe7ef;border-radius:8px;background:#fbfdff;margin-top:1rem;padding:.85rem}.aicoder-workbench__qa-head{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;margin-bottom:.75rem}.aicoder-workbench__qa-head h4{margin:0;font-size:1rem;line-height:1.25}.aicoder-workbench__qa-head span,.aicoder-workbench__qa-empty{color:var(--aicoder-muted);font-size:.82rem;line-height:1.4;margin:0}.aicoder-workbench__qa-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:.75rem}.aicoder-workbench__qa-card{display:flex;flex-direction:column;gap:.45rem;border:1px solid #dbe7ef;border-radius:7px;background:#fff;color:var(--aicoder-ink);overflow:hidden;text-decoration:none}.aicoder-workbench__qa-card img{display:block;width:100%;aspect-ratio:16/10;background:#eef3f7;object-fit:cover;object-position:top center}.aicoder-workbench__qa-card span{color:var(--aicoder-ink);font-size:.82rem;font-weight:800;line-height:1.3;padding:0 .65rem .65rem}.aicoder-workbench__actions{flex-wrap:wrap;justify-content:flex-start;margin-top:1rem}.aicoder-workbench__reply{display:block;margin-top:1rem}.aicoder-workbench__reply span{display:block;color:var(--aicoder-muted);font-size:.78rem;font-weight:800;text-transform:uppercase}.aicoder-workbench__blank{display:grid;min-height:280px;place-content:center;text-align:center}.aicoder-workbench__notice{border-radius:6px;padding:.75rem .9rem;font-weight:800}.aicoder-workbench__notice--error{border:1px solid #fecaca;background:#fff1f2;color:#991b1b}.aicoder-workbench__notice--success{border:1px solid #bbf7d0;background:#f0fdf4;color:#166534}@keyframes aicoder-workbench-spin{to{transform:rotate(360deg)}}.aicoder-workbench--compact .aicoder-workbench__layout{grid-template-columns:minmax(240px,.7fr) minmax(320px,1.3fr)}@media(max-width:980px){.aicoder-workbench__layout,.aicoder-workbench--compact .aicoder-workbench__layout{grid-template-columns:1fr;grid-template-areas:"compose" "list" "detail"}.aicoder-workbench__header,.aicoder-workbench__intro,.aicoder-workbench__detail-head{align-items:stretch;flex-direction:column}.aicoder-workbench__actions button,.aicoder-workbench__primary,.aicoder-workbench__ghost,.aicoder-workbench__danger{width:100%}}\n'],dependencies:[{kind:"directive",type:i2$1.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:i2$1.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:i2$1.NgModel,selector:"[ngModel]:not([formControlName]):not([formControl])",inputs:["name","disabled","ngModel","ngModelOptions"],outputs:["ngModelChange"],exportAs:["ngModel"]}]})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderWorkbenchComponent,decorators:[{type:Component,args:[{selector:"resolveio-client-lib-aicoder-workbench",standalone:!1,template:'<section class="aicoder-workbench" [class.aicoder-workbench--compact]="compact">\n\t@if (showHeader) {\n\t\t<header class="aicoder-workbench__header">\n\t\t\t<div>\n\t\t\t\t<p>AI Chats</p>\n\t\t\t\t<h2>{{ appName || \'AICoder App\' }}</h2>\n\t\t\t</div>\n\t\t\t<button type="button" class="aicoder-workbench__ghost" (click)="refresh()" [disabled]="isLoading || isSubmitting">Refresh</button>\n\t\t</header>\n\t}\n\n\t@if (errorMessage) {\n\t\t<div class="aicoder-workbench__notice aicoder-workbench__notice--error">{{ errorMessage }}</div>\n\t}\n\t@if (successMessage) {\n\t\t<div class="aicoder-workbench__notice aicoder-workbench__notice--success">{{ successMessage }}</div>\n\t}\n\n\t<div class="aicoder-workbench__intro">\n\t\t<div>\n\t\t\t<p class="aicoder-workbench__eyebrow">AI Assistant</p>\n\t\t\t<h3>Ask for help or tell us what to change.</h3>\n\t\t\t<p>Ask questions in plain English, or request updates. AI will answer, guide you, or prepare changes when there is work to do.</p>\n\t\t</div>\n\t\t<button type="button" class="aicoder-workbench__ghost" (click)="refresh()" [disabled]="isLoading || isSubmitting">Refresh Chats</button>\n\t</div>\n\n\t<div class="aicoder-workbench__layout">\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--compose">\n\t\t\t<h3>New AI Chat</h3>\n\t\t\t<input class="aicoder-workbench__input" [(ngModel)]="requestTitle" placeholder="Short name for this chat" />\n\t\t\t<textarea class="aicoder-workbench__textarea" rows="6" [(ngModel)]="requestMessage" placeholder="Ask a question, explain what is confusing, or describe what you want changed."></textarea>\n\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="submitRequest()" [disabled]="isSubmitting || !requestMessage.trim()">\n\t\t\t\tStart AI Chat\n\t\t\t</button>\n\t\t</section>\n\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--list">\n\t\t\t<div class="aicoder-workbench__panel-head">\n\t\t\t\t<h3>Your AI Chats</h3>\n\t\t\t\t<span>{{ visibleJobs.length }}</span>\n\t\t\t</div>\n\t\t\t@if (!jobs.length && !isLoading) {\n\t\t\t\t<p class="aicoder-workbench__empty">No AI chats yet.</p>\n\t\t\t}\n\t\t\t@if (isLoading && !jobs.length) {\n\t\t\t\t<p class="aicoder-workbench__empty">Loading AI chats...</p>\n\t\t\t}\n\t\t\t<div class="aicoder-workbench__chat-list">\n\t\t\t\t@for (job of visibleJobs; track job._id) {\n\t\t\t\t\t<button type="button" class="aicoder-workbench__chat" [class.is-active]="selectedJob?._id === job._id" (click)="selectJob(job)">\n\t\t\t\t\t\t<strong>{{ job.title || \'AI Chat\' }}</strong>\n\t\t\t\t\t\t<span>{{ friendlyJobStatus(job) }}</span>\n\t\t\t\t\t\t<small>{{ formatDate(job.updatedAt || job.createdAt) }}</small>\n\t\t\t\t\t</button>\n\t\t\t\t}\n\t\t\t</div>\n\t\t</section>\n\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--detail">\n\t\t\t@if (selectedJob) {\n\t\t\t\t<div class="aicoder-workbench__detail-head">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<p class="aicoder-workbench__eyebrow">Open Chat</p>\n\t\t\t\t\t\t<h3>{{ selectedJob.title || \'AI Chat\' }}</h3>\n\t\t\t\t\t\t<span class="aicoder-workbench__status">{{ friendlyJobStatus(selectedJob) }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<button type="button" class="aicoder-workbench__danger" (click)="deleteSelectedChat()" [disabled]="isSubmitting">Delete Chat</button>\n\t\t\t\t</div>\n\n\t\t\t\t@if (selectedJobTestSiteUrl || canSendSelectedJobToTestSite || canApproveSelectedJobLive || selectedJobHasReview) {\n\t\t\t\t\t<div class="aicoder-workbench__actions" aria-label="AI chat actions">\n\t\t\t\t\t\t@if (selectedJobTestSiteUrl) {\n\t\t\t\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="openTestSite()" [disabled]="isSubmitting">\n\t\t\t\t\t\t\t\tOpen Test Site\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (canSendSelectedJobToTestSite) {\n\t\t\t\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="sendToTestSite()" [disabled]="isSubmitting">\n\t\t\t\t\t\t\t\tSend to Test Site\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (canApproveSelectedJobLive) {\n\t\t\t\t\t\t\t<button type="button" (click)="approveAndDeployLive()" [disabled]="isSubmitting">\n\t\t\t\t\t\t\t\tPut This Version Live\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (selectedJobHasReview) {\n\t\t\t\t\t\t\t<button type="button" (click)="openPr()" [disabled]="!resolvePrUrl(selectedJob)">\n\t\t\t\t\t\t\t\tOpen Review\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t\t@else if (selectedJobNextStepMessage) {\n\t\t\t\t\t<div class="aicoder-workbench__next-step">\n\t\t\t\t\t\t@if (selectedJobIsWorking) {\n\t\t\t\t\t\t\t<span class="aicoder-workbench__spinner" aria-hidden="true"></span>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t<p>{{ selectedJobNextStepMessage }}</p>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\n\t\t\t\t@if (qaEvidence.length || selectedJobTestSiteUrl || isLoadingQaEvidence) {\n\t\t\t\t\t<section class="aicoder-workbench__qa">\n\t\t\t\t\t\t<div class="aicoder-workbench__qa-head">\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<p class="aicoder-workbench__eyebrow">QA Pictures</p>\n\t\t\t\t\t\t\t\t<h4>What changed on the test site</h4>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t@if (isLoadingQaEvidence) {\n\t\t\t\t\t\t\t\t<span>Checking...</span>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t@if (qaEvidence.length) {\n\t\t\t\t\t\t\t<div class="aicoder-workbench__qa-grid">\n\t\t\t\t\t\t\t\t@for (image of qaEvidence; track image.id) {\n\t\t\t\t\t\t\t\t\t<a class="aicoder-workbench__qa-card" [href]="resolveQaEvidenceUrl(image)" target="_blank" rel="noopener">\n\t\t\t\t\t\t\t\t\t\t<img [src]="resolveQaEvidenceUrl(image)" [alt]="image.title || \'QA picture\'" loading="lazy" />\n\t\t\t\t\t\t\t\t\t\t<span>{{ image.title || \'QA picture\' }}</span>\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@else {\n\t\t\t\t\t\t\t<p class="aicoder-workbench__qa-empty">QA pictures will appear here after the test site finishes its visual checks.</p>\n\t\t\t\t\t\t}\n\t\t\t\t\t</section>\n\t\t\t\t}\n\n\t\t\t\t<div class="aicoder-workbench__history">\n\t\t\t\t\t@if (!jobLogs.length) {\n\t\t\t\t\t\t<div class="aicoder-workbench__thinking">\n\t\t\t\t\t\t\t<span class="aicoder-workbench__spinner" aria-hidden="true"></span>\n\t\t\t\t\t\t\t<p>AI is getting started.</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t\t@for (log of jobLogs; track log.id) {\n\t\t\t\t\t\t<div class="aicoder-workbench__message" [class.aicoder-workbench__message--user]="log.role === \'user\'" [class.aicoder-workbench__message--assistant]="log.role === \'assistant\'" [class.aicoder-workbench__message--system]="log.role === \'system\'" [class.is-active]="log.active">\n\t\t\t\t\t\t\t<div class="aicoder-workbench__message-meta">\n\t\t\t\t\t\t\t\t<strong>{{ log.label }}</strong>\n\t\t\t\t\t\t\t\t@if (log.date) {\n\t\t\t\t\t\t\t\t\t<span>{{ log.date }}</span>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<p>{{ log.message }}</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\n\t\t\t\t<label class="aicoder-workbench__reply">\n\t\t\t\t\t<span>Reply to AI</span>\n\t\t\t\t\t<textarea class="aicoder-workbench__textarea" rows="4" [(ngModel)]="chatMessage" [placeholder]="selectedJobIsQuestionOnly ? \'Ask a follow-up question.\' : \'Add more details, ask for another change, or answer a question from AI.\'"></textarea>\n\t\t\t\t</label>\n\t\t\t\t<button type="button" class="aicoder-workbench__primary" (click)="sendChatMessage()" [disabled]="!selectedJobId || !chatMessage.trim() || isSubmitting">\n\t\t\t\t\tSend Reply\n\t\t\t\t</button>\n\t\t\t}\n\t\t\t@else {\n\t\t\t\t<div class="aicoder-workbench__blank">\n\t\t\t\t\t<h3>Select an AI chat</h3>\n\t\t\t\t\t<p>Open a chat from the list or start a new one.</p>\n\t\t\t\t</div>\n\t\t\t}\n\t\t</section>\n\t</div>\n\n\t@if (activeDeploys.length || recentDeploys.length) {\n\t\t<section class="aicoder-workbench__panel aicoder-workbench__panel--updates">\n\t\t\t<div class="aicoder-workbench__panel-head">\n\t\t\t\t<h3>Website Updates</h3>\n\t\t\t\t<span>{{ activeDeploys.length ? \'Running now\' : \'Recent\' }}</span>\n\t\t\t</div>\n\t\t\t<div class="aicoder-workbench__updates">\n\t\t\t\t@for (op of activeDeploys; track op.id || op.jobId) {\n\t\t\t\t\t<div class="aicoder-workbench__update">\n\t\t\t\t\t\t<strong>{{ friendlyDeployStatus(op) }}</strong>\n\t\t\t\t\t\t<span>{{ op.backend_deploy_step || op.aws_build_current_phase || op.message || \'Working on the update\' }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t\t@for (op of recentDeploys; track op.id || op.jobId) {\n\t\t\t\t\t<div class="aicoder-workbench__update">\n\t\t\t\t\t\t<strong>{{ friendlyDeployStatus(op) }}</strong>\n\t\t\t\t\t\t<span>{{ op.message || op.backend_deploy_step || \'Update finished\' }}</span>\n\t\t\t\t\t\t<small>{{ formatDate(op.updatedAt || op.createdAt) }}</small>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t</section>\n\t}\n</section>\n',styles:['.aicoder-workbench{--aicoder-accent: #2f7d73;--aicoder-accent-dark: #155e57;--aicoder-border: #d8e2ec;--aicoder-ink: #1f2933;--aicoder-muted: #64748b;--aicoder-soft: #f5f8fb;display:flex;flex-direction:column;gap:1rem;color:var(--aicoder-ink)}.aicoder-workbench__header,.aicoder-workbench__intro,.aicoder-workbench__panel-head,.aicoder-workbench__detail-head,.aicoder-workbench__actions{display:flex;align-items:center;justify-content:space-between;gap:1rem}.aicoder-workbench__header p,.aicoder-workbench__eyebrow{margin:0 0 .25rem;color:var(--aicoder-accent-dark);font-size:.75rem;font-weight:800;letter-spacing:0;text-transform:uppercase}.aicoder-workbench__header h2,.aicoder-workbench__intro h3,.aicoder-workbench__panel h3{margin:0;font-weight:800;line-height:1.2}.aicoder-workbench__header h2{font-size:1.35rem}.aicoder-workbench__intro{border:1px solid var(--aicoder-border);border-radius:8px;background:#fff;padding:1rem}.aicoder-workbench__intro h3{font-size:1.25rem}.aicoder-workbench__intro p:not(.aicoder-workbench__eyebrow),.aicoder-workbench__blank p,.aicoder-workbench__empty{margin:.35rem 0 0;color:var(--aicoder-muted);line-height:1.45}.aicoder-workbench__layout{display:grid;grid-template-columns:minmax(260px,.75fr) minmax(320px,1.35fr);grid-template-areas:"compose detail" "list detail";gap:1rem;align-items:start}.aicoder-workbench__panel{min-width:0;border:1px solid var(--aicoder-border);border-radius:8px;background:#fff;padding:1rem}.aicoder-workbench__panel--compose{grid-area:compose}.aicoder-workbench__panel--list{grid-area:list}.aicoder-workbench__panel--detail{grid-area:detail}.aicoder-workbench__panel--updates{display:grid;gap:.75rem}.aicoder-workbench__panel-head span,.aicoder-workbench__status{display:inline-flex;align-items:center;border-radius:999px;background:#e7f4f0;color:var(--aicoder-accent-dark);font-size:.78rem;font-weight:800;padding:.25rem .6rem}.aicoder-workbench__next-step,.aicoder-workbench__thinking{display:flex;align-items:center;gap:.65rem;border:1px solid #dbe7ef;border-radius:8px;background:#f7fafc;color:var(--aicoder-muted);margin-top:1rem;padding:.85rem}.aicoder-workbench__next-step p,.aicoder-workbench__thinking p{margin:0;line-height:1.4}.aicoder-workbench__spinner{display:inline-block;width:1rem;height:1rem;border:2px solid #bfd2df;border-top-color:var(--aicoder-accent);border-radius:999px;flex:0 0 auto;animation:aicoder-workbench-spin .85s linear infinite}.aicoder-workbench__input,.aicoder-workbench__textarea{width:100%;border:1px solid #cbd5e1;border-radius:6px;background:#fff;color:var(--aicoder-ink);padding:.7rem .8rem}.aicoder-workbench__textarea{resize:vertical}.aicoder-workbench__input,.aicoder-workbench__textarea{margin:.75rem 0}.aicoder-workbench__ghost,.aicoder-workbench__primary,.aicoder-workbench__danger,.aicoder-workbench__actions button{border:1px solid #cbd5e1;border-radius:6px;background:#fff;color:var(--aicoder-ink);cursor:pointer;font:inherit;font-weight:800;min-height:40px;padding:.6rem .85rem;text-decoration:none}.aicoder-workbench__primary{border-color:var(--aicoder-accent);background:var(--aicoder-accent);color:#fff}.aicoder-workbench__danger{border-color:#fecaca;color:#991b1b}.aicoder-workbench button:disabled{cursor:not-allowed;opacity:.55}.aicoder-workbench__chat-list,.aicoder-workbench__history,.aicoder-workbench__updates{display:flex;flex-direction:column;gap:.5rem}.aicoder-workbench__chat-list,.aicoder-workbench__history{max-height:520px;overflow:auto}.aicoder-workbench__chat,.aicoder-workbench__message,.aicoder-workbench__update{display:flex;flex-direction:column;align-items:flex-start;gap:.25rem;width:100%;border:1px solid #e2e8f0;border-radius:7px;background:var(--aicoder-soft);padding:.8rem;text-align:left}.aicoder-workbench__message{background:#fff}.aicoder-workbench__message--user{border-color:#c9d8e6;background:#f8fafc}.aicoder-workbench__message--assistant{border-color:#b7ded3;background:#f4fbf9}.aicoder-workbench__message--system{background:#fbfdff}.aicoder-workbench__message.is-active{border-color:var(--aicoder-accent)}.aicoder-workbench__message-meta{display:flex;align-items:center;justify-content:space-between;gap:.75rem;width:100%;color:var(--aicoder-muted);font-size:.8rem;line-height:1.35}.aicoder-workbench__message-meta strong{color:var(--aicoder-ink);font-size:.82rem}.aicoder-workbench__chat{cursor:pointer}.aicoder-workbench__chat.is-active{border-color:var(--aicoder-accent);background:#ecfdf5}.aicoder-workbench__chat span,.aicoder-workbench__chat small,.aicoder-workbench__message span,.aicoder-workbench__update span,.aicoder-workbench__update small{color:var(--aicoder-muted);font-size:.82rem;line-height:1.35}.aicoder-workbench__message p{margin:0;white-space:pre-wrap;overflow-wrap:anywhere}.aicoder-workbench__history{min-height:260px;margin:.75rem 0}.aicoder-workbench__qa{border:1px solid #dbe7ef;border-radius:8px;background:#fbfdff;margin-top:1rem;padding:.85rem}.aicoder-workbench__qa-head{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;margin-bottom:.75rem}.aicoder-workbench__qa-head h4{margin:0;font-size:1rem;line-height:1.25}.aicoder-workbench__qa-head span,.aicoder-workbench__qa-empty{color:var(--aicoder-muted);font-size:.82rem;line-height:1.4;margin:0}.aicoder-workbench__qa-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:.75rem}.aicoder-workbench__qa-card{display:flex;flex-direction:column;gap:.45rem;border:1px solid #dbe7ef;border-radius:7px;background:#fff;color:var(--aicoder-ink);overflow:hidden;text-decoration:none}.aicoder-workbench__qa-card img{display:block;width:100%;aspect-ratio:16/10;background:#eef3f7;object-fit:cover;object-position:top center}.aicoder-workbench__qa-card span{color:var(--aicoder-ink);font-size:.82rem;font-weight:800;line-height:1.3;padding:0 .65rem .65rem}.aicoder-workbench__actions{flex-wrap:wrap;justify-content:flex-start;margin-top:1rem}.aicoder-workbench__reply{display:block;margin-top:1rem}.aicoder-workbench__reply span{display:block;color:var(--aicoder-muted);font-size:.78rem;font-weight:800;text-transform:uppercase}.aicoder-workbench__blank{display:grid;min-height:280px;place-content:center;text-align:center}.aicoder-workbench__notice{border-radius:6px;padding:.75rem .9rem;font-weight:800}.aicoder-workbench__notice--error{border:1px solid #fecaca;background:#fff1f2;color:#991b1b}.aicoder-workbench__notice--success{border:1px solid #bbf7d0;background:#f0fdf4;color:#166534}@keyframes aicoder-workbench-spin{to{transform:rotate(360deg)}}.aicoder-workbench--compact .aicoder-workbench__layout{grid-template-columns:minmax(240px,.7fr) minmax(320px,1.3fr)}@media(max-width:980px){.aicoder-workbench__layout,.aicoder-workbench--compact .aicoder-workbench__layout{grid-template-columns:1fr;grid-template-areas:"compose" "list" "detail"}.aicoder-workbench__header,.aicoder-workbench__intro,.aicoder-workbench__detail-head{align-items:stretch;flex-direction:column}.aicoder-workbench__actions button,.aicoder-workbench__primary,.aicoder-workbench__ghost,.aicoder-workbench__danger{width:100%}}\n']}]}],ctorParameters:()=>[{type:AICoderWorkbenchApiService}],propDecorators:{appId:[{type:Input}],appName:[{type:Input}],apiBase:[{type:Input}],appToken:[{type:Input}],defaultTab:[{type:Input}],compact:[{type:Input}],showHeader:[{type:Input}],autoRefresh:[{type:Input}]}});class AICoderDashboardModule{static"ɵfac"=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,deps:[],target:i0.ɵɵFactoryTarget.NgModule});static"ɵmod"=i0.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,declarations:[AICoderDashboardComponent,AICoderWorkbenchComponent],imports:[CommonModule,FormsModule,HttpClientModule],exports:[AICoderDashboardComponent,AICoderWorkbenchComponent]});static"ɵinj"=i0.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,imports:[CommonModule,FormsModule,HttpClientModule]})}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"21.1.2",ngImport:i0,type:AICoderDashboardModule,decorators:[{type:NgModule,args:[{imports:[CommonModule,FormsModule,HttpClientModule],declarations:[AICoderDashboardComponent,AICoderWorkbenchComponent],exports:[AICoderDashboardComponent,AICoderWorkbenchComponent]}]}]});export{AICoderDashboardComponent,AICoderDashboardModule,AICoderDashboardService,AICoderWorkbenchApiService,AICoderWorkbenchComponent};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resolveio/client-lib-aicoder-dashboard",
3
- "version": "21.0.10",
3
+ "version": "21.0.11",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^21.0.0",
6
6
  "@angular/core": "^21.0.0",
@@ -88,6 +88,18 @@ interface AICoderWorkbenchJob {
88
88
  agentNotes?: Record<string, string>;
89
89
  };
90
90
  }
91
+ interface AICoderWorkbenchQaEvidence {
92
+ id: string;
93
+ title: string;
94
+ route?: string;
95
+ viewport?: string;
96
+ width?: number;
97
+ height?: number;
98
+ content_type?: string;
99
+ url?: string;
100
+ data_url?: string;
101
+ created_at?: string;
102
+ }
91
103
  interface AICoderWorkbenchConversationEntry {
92
104
  role?: 'user' | 'assistant' | string;
93
105
  _id?: string;
@@ -211,10 +223,12 @@ declare class AICoderWorkbenchComponent implements OnInit, OnChanges, OnDestroy
211
223
  jobs: AICoderWorkbenchJob[];
212
224
  selectedJob: AICoderWorkbenchJob | null;
213
225
  jobLogs: AICoderWorkbenchTimelineItem[];
226
+ qaEvidence: AICoderWorkbenchQaEvidence[];
214
227
  deployOperations: AICoderWorkbenchDeployOperation[];
215
228
  deployJobs: AICoderWorkbenchDeployJob[];
216
229
  gitStatus: AICoderWorkbenchGitStatus | null;
217
230
  isLoading: boolean;
231
+ isLoadingQaEvidence: boolean;
218
232
  isSubmitting: boolean;
219
233
  errorMessage: string;
220
234
  successMessage: string;
@@ -249,6 +263,7 @@ declare class AICoderWorkbenchComponent implements OnInit, OnChanges, OnDestroy
249
263
  openTestSite(): void;
250
264
  loadJobs(): void;
251
265
  loadJobLogs(jobId: string): void;
266
+ loadJobQaEvidence(jobId: string): void;
252
267
  loadDeployHistory(): void;
253
268
  loadGitStatus(): void;
254
269
  get normalizedAppId(): string;
@@ -272,6 +287,7 @@ declare class AICoderWorkbenchComponent implements OnInit, OnChanges, OnDestroy
272
287
  friendlyJobStatus(job: AICoderWorkbenchJob | null): string;
273
288
  friendlyDeployStatus(op: AICoderWorkbenchDeployOperation): string;
274
289
  formatDate(value: any): string;
290
+ resolveQaEvidenceUrl(entry: AICoderWorkbenchQaEvidence | null): string;
275
291
  private buildTimeline;
276
292
  private extractUserRequest;
277
293
  private sanitizeUserFacingMessage;
@@ -307,4 +323,4 @@ declare class AICoderDashboardModule {
307
323
  }
308
324
 
309
325
  export { AICoderDashboardComponent, AICoderDashboardModule, AICoderDashboardService, AICoderWorkbenchApiService, AICoderWorkbenchComponent };
310
- export type { AICoderDashboardSummary, AICoderDashboardTab, AICoderTier, AICoderTierOption, AICoderWorkbenchConversationEntry, AICoderWorkbenchDeployJob, AICoderWorkbenchDeployOperation, AICoderWorkbenchGitStatus, AICoderWorkbenchJob, AICoderWorkbenchLog, AICoderWorkbenchTab };
326
+ export type { AICoderDashboardSummary, AICoderDashboardTab, AICoderTier, AICoderTierOption, AICoderWorkbenchConversationEntry, AICoderWorkbenchDeployJob, AICoderWorkbenchDeployOperation, AICoderWorkbenchGitStatus, AICoderWorkbenchJob, AICoderWorkbenchLog, AICoderWorkbenchQaEvidence, AICoderWorkbenchTab };