@adcops/autocore-react 3.3.84 → 3.3.87

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.
Files changed (86) hide show
  1. package/dist/components/ValueInput.css +9 -12
  2. package/dist/components/ValueInput.d.ts +45 -154
  3. package/dist/components/ValueInput.d.ts.map +1 -1
  4. package/dist/components/ValueInput.js +1 -1
  5. package/dist/components/ams/AssetEditDialog.d.ts.map +1 -1
  6. package/dist/components/ams/AssetEditDialog.js +1 -1
  7. package/dist/components/forms/FormRow.d.ts +20 -0
  8. package/dist/components/forms/FormRow.d.ts.map +1 -0
  9. package/dist/components/forms/FormRow.js +1 -0
  10. package/dist/components/forms/FormSection.d.ts +19 -0
  11. package/dist/components/forms/FormSection.d.ts.map +1 -0
  12. package/dist/components/forms/FormSection.js +1 -0
  13. package/dist/components/forms/forms.css +89 -0
  14. package/dist/components/forms/index.d.ts +3 -0
  15. package/dist/components/forms/index.d.ts.map +1 -0
  16. package/dist/components/forms/index.js +1 -0
  17. package/dist/components/tis-editor/TisConfigEditor.css +121 -0
  18. package/dist/components/tis-editor/TisConfigEditor.d.ts +28 -0
  19. package/dist/components/tis-editor/TisConfigEditor.d.ts.map +1 -0
  20. package/dist/components/tis-editor/TisConfigEditor.js +1 -0
  21. package/dist/components/tis-editor/editor/AnalysisEditor.d.ts +7 -0
  22. package/dist/components/tis-editor/editor/AnalysisEditor.d.ts.map +1 -0
  23. package/dist/components/tis-editor/editor/AnalysisEditor.js +1 -0
  24. package/dist/components/tis-editor/editor/AssetRefsEditor.d.ts +10 -0
  25. package/dist/components/tis-editor/editor/AssetRefsEditor.d.ts.map +1 -0
  26. package/dist/components/tis-editor/editor/AssetRefsEditor.js +1 -0
  27. package/dist/components/tis-editor/editor/ChartViewDialog.d.ts +16 -0
  28. package/dist/components/tis-editor/editor/ChartViewDialog.d.ts.map +1 -0
  29. package/dist/components/tis-editor/editor/ChartViewDialog.js +1 -0
  30. package/dist/components/tis-editor/editor/FieldArrayEditor.d.ts +8 -0
  31. package/dist/components/tis-editor/editor/FieldArrayEditor.d.ts.map +1 -0
  32. package/dist/components/tis-editor/editor/FieldArrayEditor.js +1 -0
  33. package/dist/components/tis-editor/editor/IdentitySection.d.ts +7 -0
  34. package/dist/components/tis-editor/editor/IdentitySection.d.ts.map +1 -0
  35. package/dist/components/tis-editor/editor/IdentitySection.js +1 -0
  36. package/dist/components/tis-editor/editor/MethodFormEditor.d.ts +20 -0
  37. package/dist/components/tis-editor/editor/MethodFormEditor.d.ts.map +1 -0
  38. package/dist/components/tis-editor/editor/MethodFormEditor.js +1 -0
  39. package/dist/components/tis-editor/editor/RawDataEditor.d.ts +7 -0
  40. package/dist/components/tis-editor/editor/RawDataEditor.d.ts.map +1 -0
  41. package/dist/components/tis-editor/editor/RawDataEditor.js +1 -0
  42. package/dist/components/tis-editor/editor/SaveDiffDialog.d.ts +22 -0
  43. package/dist/components/tis-editor/editor/SaveDiffDialog.d.ts.map +1 -0
  44. package/dist/components/tis-editor/editor/SaveDiffDialog.js +1 -0
  45. package/dist/components/tis-editor/editor/TestFieldDialog.d.ts +11 -0
  46. package/dist/components/tis-editor/editor/TestFieldDialog.d.ts.map +1 -0
  47. package/dist/components/tis-editor/editor/TestFieldDialog.js +1 -0
  48. package/dist/components/tis-editor/editor/ViewsEditor.d.ts +7 -0
  49. package/dist/components/tis-editor/editor/ViewsEditor.d.ts.map +1 -0
  50. package/dist/components/tis-editor/editor/ViewsEditor.js +1 -0
  51. package/dist/components/tis-editor/types.d.ts +78 -0
  52. package/dist/components/tis-editor/types.d.ts.map +1 -0
  53. package/dist/components/tis-editor/types.js +1 -0
  54. package/dist/components/tis-editor/validation.d.ts +20 -0
  55. package/dist/components/tis-editor/validation.d.ts.map +1 -0
  56. package/dist/components/tis-editor/validation.js +1 -0
  57. package/dist/hooks/useAmsAssetTypes.d.ts +23 -0
  58. package/dist/hooks/useAmsAssetTypes.d.ts.map +1 -0
  59. package/dist/hooks/useAmsAssetTypes.js +1 -0
  60. package/dist/hooks/useTisConfig.d.ts +51 -0
  61. package/dist/hooks/useTisConfig.d.ts.map +1 -0
  62. package/dist/hooks/useTisConfig.js +1 -0
  63. package/package.json +9 -3
  64. package/src/components/ValueInput.css +9 -12
  65. package/src/components/ValueInput.tsx +132 -317
  66. package/src/components/ams/AssetEditDialog.tsx +357 -20
  67. package/src/components/forms/FormRow.tsx +37 -0
  68. package/src/components/forms/FormSection.tsx +39 -0
  69. package/src/components/forms/forms.css +89 -0
  70. package/src/components/forms/index.ts +2 -0
  71. package/src/components/tis-editor/TisConfigEditor.css +121 -0
  72. package/src/components/tis-editor/TisConfigEditor.tsx +321 -0
  73. package/src/components/tis-editor/editor/AnalysisEditor.tsx +54 -0
  74. package/src/components/tis-editor/editor/AssetRefsEditor.tsx +187 -0
  75. package/src/components/tis-editor/editor/ChartViewDialog.tsx +170 -0
  76. package/src/components/tis-editor/editor/FieldArrayEditor.tsx +131 -0
  77. package/src/components/tis-editor/editor/IdentitySection.tsx +36 -0
  78. package/src/components/tis-editor/editor/MethodFormEditor.tsx +176 -0
  79. package/src/components/tis-editor/editor/RawDataEditor.tsx +117 -0
  80. package/src/components/tis-editor/editor/SaveDiffDialog.tsx +160 -0
  81. package/src/components/tis-editor/editor/TestFieldDialog.tsx +134 -0
  82. package/src/components/tis-editor/editor/ViewsEditor.tsx +101 -0
  83. package/src/components/tis-editor/types.ts +95 -0
  84. package/src/components/tis-editor/validation.ts +104 -0
  85. package/src/hooks/useAmsAssetTypes.ts +70 -0
  86. package/src/hooks/useTisConfig.ts +164 -0
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useEffect,useMemo,useState}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Button}from"primereact/button";import{InputText}from"primereact/inputtext";import{Dialog}from"primereact/dialog";import{useContext}from"react";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useTisConfig}from"../../hooks/useTisConfig";import{useAmsAssetTypes}from"../../hooks/useAmsAssetTypes";import{MethodFormEditor}from"./editor/MethodFormEditor";import{SaveDiffDialog}from"./editor/SaveDiffDialog";import"./TisConfigEditor.css";const EMPTY_METHOD={label:"",description:"",project_fields:[],config_fields:[],cycle_fields:[],results_fields:[],views:{},asset_refs:[]};export const TisConfigEditor=({projectId:e,invoker:t})=>{const s=useContext(EventEmitterContext),i=t??(async(e,t)=>await s.invoke(e,MessageType.Request,t)),o=useTisConfig(e,{invoker:i}),a=useAmsAssetTypes({invoker:i}),[n,r]=useState(null),[l,d]=useState(null),[c,m]=useState(!1),[f,p]=useState(!1),[h,u]=useState(!1),[_,g]=useState(""),x=useMemo(()=>o.config?Object.entries(o.config.methods).map(([e,t])=>({id:e,label:t?.label??""})):[],[o.config]);useEffect(()=>{if(!n&&o.config){const e=o.config.defaultMethodId||Object.keys(o.config.methods)[0]||null;r(e)}},[o.config,n]),useEffect(()=>{d(null)},[n]);return _jsxs("div",{className:"tis-editor",children:[_jsxs("header",{className:"tis-editor__header",children:[_jsxs("h2",{children:["Test Methods"," ",o.config?.dirty&&_jsx("span",{className:"tis-editor__dirty-pill",children:"unsaved"})]}),_jsxs("div",{className:"tis-editor__header-actions",children:[_jsx(Button,{label:"Save…",icon:"pi pi-save",disabled:c||!o.config?.dirty,onClick:()=>p(!0)}),_jsx(Button,{label:"Revert",icon:"pi pi-undo",className:"p-button-secondary",disabled:c||!o.config?.dirty,onClick:async()=>{if(window.confirm("Discard all in-progress edits? This cannot be undone.")){m(!0);try{await o.revert()}catch(e){d(String(e?.message??e))}finally{m(!1)}}}})]})]}),o.error&&_jsxs("div",{className:"tis-editor__error",children:[_jsx("strong",{children:"Error:"})," ",_jsx("pre",{children:o.error})]}),_jsxs("div",{className:"tis-editor__body",children:[_jsxs("aside",{className:"tis-editor__sidebar",children:[_jsxs("div",{className:"tis-editor__sidebar-actions",children:[_jsx(Button,{label:"New",icon:"pi pi-plus",disabled:c,onClick:()=>u(!0)}),_jsx(Button,{label:"Duplicate",icon:"pi pi-clone",className:"p-button-secondary",disabled:c||!n,onClick:async()=>{if(!n||!o.config)return;const e=o.config.methods[n];if(!e)return;let t=`${n}_copy`,s=2;for(;o.config.methods[t];)t=`${n}_copy_${s++}`;m(!0);try{await o.putMethod(t,JSON.parse(JSON.stringify(e))),r(t)}catch(e){d(String(e?.message??e))}finally{m(!1)}}}),_jsx(Button,{label:"Delete",icon:"pi pi-trash",className:"p-button-danger",disabled:c||!n,onClick:async()=>{if(n&&window.confirm(`Remove method "${n}"? This is staged — Save persists it.`)){m(!0);try{await o.removeMethod(n),r(null)}catch(e){d(String(e?.message??e))}finally{m(!1)}}}})]}),_jsxs(DataTable,{value:x,selection:x.find(e=>e.id===n)??null,onSelectionChange:e=>r(e.value?.id??null),selectionMode:"single",dataKey:"id",scrollable:!0,scrollHeight:"flex",emptyMessage:o.loading?"Loading…":"No test methods defined.",children:[_jsx(Column,{field:"id",header:"Method ID"}),_jsx(Column,{field:"label",header:"Label"})]})]}),_jsx("section",{className:"tis-editor__detail",children:n&&o.config?.methods[n]?_jsxs(_Fragment,{children:[l&&_jsx("div",{className:"tis-editor__error",children:_jsx("pre",{children:l})}),_jsx(MethodFormEditor,{methodId:n,method:o.config.methods[n],onApply:async e=>{if(n){m(!0);try{await o.putMethod(n,e),d(null)}catch(e){d(String(e?.message??e))}finally{m(!1)}}},busy:c,knownAssetTypes:a.types})]}):_jsx("div",{className:"tis-editor__empty",children:"Select a test method on the left, or create a new one."})})]}),_jsxs(Dialog,{header:"New Test Method",visible:h,onHide:()=>u(!1),style:{width:"24rem"},children:[_jsxs("label",{className:"tis-editor__new-method-label",children:["Method ID",_jsx(InputText,{value:_,onChange:e=>g(e.target.value),placeholder:"e.g. translational_traction",autoFocus:!0})]}),_jsx("small",{children:"Canonical key — appears in wire payloads, on-disk paths, and generated code."}),_jsxs("div",{style:{display:"flex",gap:"0.5rem",justifyContent:"flex-end",marginTop:"1rem"},children:[_jsx(Button,{label:"Cancel",className:"p-button-text",onClick:()=>u(!1)}),_jsx(Button,{label:"Create",disabled:!_.trim()||c,onClick:async()=>{const e=_.trim();if(e)if(o.config?.methods[e])d(`A method named "${e}" already exists.`);else{m(!0);try{await o.putMethod(e,EMPTY_METHOD),r(e),u(!1),g("")}catch(e){d(String(e?.message??e))}finally{m(!1)}}}})]})]}),_jsx(SaveDiffDialog,{visible:f,staged:o.config?.methods??{},invoker:i,onConfirm:async()=>{m(!0);try{await o.save(),p(!1)}catch(e){d(String(e?.message??e))}finally{m(!1)}},onCancel:()=>p(!1)})]})};export default TisConfigEditor;
@@ -0,0 +1,7 @@
1
+ import type { TestMethod } from '../types';
2
+ export interface AnalysisEditorProps {
3
+ method: TestMethod;
4
+ onChange: (next: TestMethod) => void;
5
+ }
6
+ export declare const AnalysisEditor: React.FC<AnalysisEditorProps>;
7
+ //# sourceMappingURL=AnalysisEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnalysisEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/AnalysisEditor.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAiB,UAAU,EAAE,MAAM,UAAU,CAAC;AAE1D,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACxC;AAID,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAwCxD,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,Fragment as _Fragment,jsxs as _jsxs}from"react/jsx-runtime";import{InputText}from"primereact/inputtext";import{Checkbox}from"primereact/checkbox";import{FormSection}from"../../forms/FormSection";import{FormRow}from"../../forms/FormRow";const empty=()=>({script:"",function:""});export const AnalysisEditor=({method:e,onChange:t})=>{const o=e.analysis,n=!!o,r=o=>{t({...e,analysis:o})};return _jsxs(FormSection,{title:"Analysis Hook",description:"Optional post-cycle Python entry point. Codegen wires this into the method's TestManager.",actions:_jsx(Checkbox,{checked:n,onChange:e=>r(e.checked?empty():null)}),children:[!n&&_jsx("small",{children:"Analysis hook disabled. Tick the box to enable."}),n&&o&&_jsxs(_Fragment,{children:[_jsx(FormRow,{label:"Script",required:!0,hint:"Path under autocore-python's scripts directory.",children:_jsx(InputText,{value:o.script,onChange:e=>r({...o,script:e.target.value}),placeholder:"e.g. analysis/traction_v1.py"})}),_jsx(FormRow,{label:"Function",required:!0,hint:"Entry-point name within the script.",children:_jsx(InputText,{value:o.function,onChange:e=>r({...o,function:e.target.value}),placeholder:"e.g. analyze_cycle"})})]})]})};
@@ -0,0 +1,10 @@
1
+ import type { TestMethod } from '../types';
2
+ export interface AssetRefsEditorProps {
3
+ method: TestMethod;
4
+ onChange: (next: TestMethod) => void;
5
+ /** Asset types known to AMS, supplied by the host. Empty array = use a
6
+ * free-form text field. Phase 3 wires this up to ams.list_schemas. */
7
+ knownAssetTypes?: string[];
8
+ }
9
+ export declare const AssetRefsEditor: React.FC<AssetRefsEditorProps>;
10
+ //# sourceMappingURL=AssetRefsEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssetRefsEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/AssetRefsEditor.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAY,UAAU,EAAE,MAAM,UAAU,CAAC;AAarD,MAAM,WAAW,oBAAoB;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACrC;2EACuE;IACvE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAOD,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAsJ1D,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useState}from"react";import{Dialog}from"primereact/dialog";import{Button}from"primereact/button";import{InputText}from"primereact/inputtext";import{InputTextarea}from"primereact/inputtextarea";import{Dropdown}from"primereact/dropdown";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{FormSection}from"../../forms/FormSection";import{FormRow}from"../../forms/FormRow";const SELECT_OPTIONS=[{label:"By location",value:"by_location"},{label:"By id field",value:"by_id_field"}],CALIBRATION_OPTIONS=[{label:"Ignore",value:"ignore"},{label:"Warn (default)",value:"warn"},{label:"Require",value:"require"}],blank=()=>({field:"",asset_type:"",select:"by_location",calibration_required:"warn"});export const AssetRefsEditor=({method:e,onChange:t,knownAssetTypes:l=[]})=>{const o=e.asset_refs??[],[a,r]=useState(!1),[i,s]=useState(null),[n,d]=useState(blank()),[u,c]=useState(null),m=l.length>0?l.map(e=>({label:e,value:e})):null;return _jsxs(_Fragment,{children:[_jsx(FormSection,{title:"Asset References",description:"AMS dependencies resolved at start_test time and snapshotted into test.json.",actions:_jsx(Button,{label:"Add asset_ref",icon:"pi pi-plus",size:"small",onClick:()=>{s(null),d(blank()),c(null),r(!0)}}),children:_jsxs(DataTable,{value:o,dataKey:"field",emptyMessage:"No asset_refs declared.",children:[_jsx(Column,{field:"field",header:"Field"}),_jsx(Column,{field:"asset_type",header:"Asset type"}),_jsx(Column,{field:"select",header:"Select",style:{width:"7rem"}}),_jsx(Column,{header:"Locator",body:e=>"by_location"===e.select?e.location:e.from}),_jsx(Column,{field:"calibration_required",header:"Calibration",style:{width:"7rem"}}),_jsx(Column,{header:"",body:(l,a)=>_jsxs("div",{style:{display:"flex",gap:"0.25rem"},children:[_jsx(Button,{icon:"pi pi-pencil",className:"p-button-text p-button-sm",onClick:()=>{return e=a.rowIndex,s(e),d({...o[e]}),c(null),void r(!0);var e}}),_jsx(Button,{icon:"pi pi-trash",className:"p-button-text p-button-danger p-button-sm",onClick:()=>{return l=a.rowIndex,void(window.confirm(`Remove asset_ref "${o[l].field}"?`)&&t({...e,asset_refs:o.filter((e,t)=>t!==l)}));var l}})]}),style:{width:"6rem"}})]})}),_jsxs(Dialog,{header:null===i?"New asset_ref":`Edit asset_ref: ${o[i??0]?.field}`,visible:a,onHide:()=>r(!1),style:{width:"40rem"},children:[u&&_jsx("div",{style:{color:"#dc2626",marginBottom:"0.5rem"},children:u}),_jsx(FormRow,{label:"Field",required:!0,hint:"Key under test.json::asset_snapshot.",children:_jsx(InputText,{value:n.field,onChange:e=>d({...n,field:e.target.value})})}),_jsx(FormRow,{label:"Asset type",required:!0,children:m?_jsx(Dropdown,{value:n.asset_type,options:m,onChange:e=>d({...n,asset_type:e.value}),editable:!0}):_jsx(InputText,{value:n.asset_type,onChange:e=>d({...n,asset_type:e.target.value}),placeholder:"e.g. load_cell"})}),_jsx(FormRow,{label:"Select",required:!0,children:_jsx(Dropdown,{value:n.select,options:SELECT_OPTIONS,onChange:e=>d({...n,select:e.value})})}),"by_location"===n.select&&_jsx(FormRow,{label:"Location",required:!0,hint:"AMS location key (e.g. tsdr).",children:_jsx(InputText,{value:n.location??"",onChange:e=>d({...n,location:e.target.value})})}),"by_id_field"===n.select&&_jsx(FormRow,{label:"From",required:!0,hint:"Dotted config path (e.g. config.surface_asset_id).",children:_jsx(InputText,{value:n.from??"",onChange:e=>d({...n,from:e.target.value})})}),_jsx(FormRow,{label:"Calibration policy",children:_jsx(Dropdown,{value:n.calibration_required??"warn",options:CALIBRATION_OPTIONS,onChange:e=>d({...n,calibration_required:e.value})})}),_jsx(FormRow,{label:"Label",children:_jsx(InputText,{value:n.label??"",onChange:e=>d({...n,label:e.target.value||void 0})})}),_jsx(FormRow,{label:"Description",children:_jsx(InputTextarea,{rows:2,value:n.description??"",onChange:e=>d({...n,description:e.target.value||void 0})})}),_jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem",marginTop:"1rem"},children:[_jsx(Button,{label:"Cancel",className:"p-button-text",onClick:()=>r(!1)}),_jsx(Button,{label:"Save",onClick:()=>{const l=(a=n).field.trim()?a.asset_type.trim()?"by_location"!==a.select||a.location?.trim()?"by_id_field"!==a.select||a.from?.trim()?o.some((e,t)=>e.field===a.field&&t!==i)?`An asset_ref for field "${a.field}" already exists.`:null:"From-path is required when select=by_id_field.":"Location is required when select=by_location.":"Asset type is required.":"Field name is required.";var a;if(l)return void c(l);const s=[...o];null===i?s.push(n):s[i]=n,t({...e,asset_refs:s}),r(!1)}})]})]})]})};
@@ -0,0 +1,16 @@
1
+ import type { ChartView } from '../types';
2
+ export interface ChartViewDialogProps {
3
+ visible: boolean;
4
+ initial: {
5
+ viewId: string;
6
+ view: ChartView;
7
+ } | null;
8
+ onCancel: () => void;
9
+ onSave: (viewId: string, view: ChartView) => void;
10
+ /** Known field/column names; offered as datalist suggestions. */
11
+ knownKeys: string[];
12
+ /** Other view ids already in use — for uniqueness validation. */
13
+ siblingIds: string[];
14
+ }
15
+ export declare const ChartViewDialog: React.FC<ChartViewDialogProps>;
16
+ //# sourceMappingURL=ChartViewDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChartViewDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/ChartViewDialog.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAA0B,SAAS,EAAE,MAAM,UAAU,CAAC;AAYlE,MAAM,WAAW,oBAAoB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,GAAG,IAAI,CAAC;IACpD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IAClD,iEAAiE;IACjE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,iEAAiE;IACjE,UAAU,EAAE,MAAM,EAAE,CAAC;CACxB;AAQD,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAsI1D,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import{useEffect,useState}from"react";import{Dialog}from"primereact/dialog";import{Button}from"primereact/button";import{InputText}from"primereact/inputtext";import{Dropdown}from"primereact/dropdown";import{FormRow}from"../../forms/FormRow";const VIEW_TYPES=[{label:"Cycle scatter",value:"cycle_scatter"},{label:"Raw trace",value:"raw_trace"}],Y_AXES=[{label:"Left",value:"left"},{label:"Right",value:"right"}],blank=()=>({title:"",type:"cycle_scatter",x:{field:"",label:""},y:[]});export const ChartViewDialog=({visible:e,initial:t,onCancel:l,onSave:a,knownKeys:i,siblingIds:n})=>{const[r,s]=useState(""),[o,c]=useState(blank()),[u,m]=useState(null);useEffect(()=>{e&&(s(t?.viewId??""),c(t?JSON.parse(JSON.stringify(t.view)):blank()),m(null))},[e,t]);const d="raw_trace"===o.type,p=d?"column":"field",x=e=>{c({...o,x:{...o.x,...e}})},_=(e,t)=>{const l=[...o.y];l[e]={...l[e],...t},c({...o,y:l})};return _jsxs(Dialog,{header:t?`Edit view: ${t.viewId}`:"New chart view",visible:e,onHide:l,style:{width:"42rem"},children:[u&&_jsx("div",{style:{color:"#dc2626",marginBottom:"0.5rem"},children:u}),_jsx(FormRow,{label:"View ID",required:!0,hint:"Stable key (e.g. cof_scatter). Cannot collide with other views.",children:_jsx(InputText,{value:r,onChange:e=>s(e.target.value)})}),_jsx(FormRow,{label:"Title",children:_jsx(InputText,{value:o.title??"",onChange:e=>c({...o,title:e.target.value})})}),_jsx(FormRow,{label:"Type",required:!0,children:_jsx(Dropdown,{value:o.type,options:VIEW_TYPES,onChange:e=>c({...o,type:e.value})})}),_jsx(FormRow,{label:d?"X column":"X field",required:!0,children:_jsx(InputText,{value:o.x[p]??"",onChange:e=>x({[p]:e.target.value}),list:"known-keys"})}),_jsx(FormRow,{label:"X label",children:_jsx(InputText,{value:o.x.label??"",onChange:e=>x({label:e.target.value})})}),_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",margin:"1rem 0 0.5rem"},children:[_jsx("strong",{children:"Y series"}),_jsx(Button,{label:"Add series",icon:"pi pi-plus",size:"small",onClick:()=>{c({...o,y:[...o.y,{[p]:"",label:"",y_axis:"left"}]})}})]}),0===o.y.length&&_jsx("small",{children:"No series — add at least one."}),o.y.map((e,t)=>_jsxs("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr 6rem 2rem",gap:"0.5rem",marginBottom:"0.25rem",alignItems:"center"},children:[_jsx(InputText,{placeholder:d?"column":"field",value:e[p]??"",onChange:e=>_(t,{[p]:e.target.value}),list:"known-keys"}),_jsx(InputText,{placeholder:"label",value:e.label??"",onChange:e=>_(t,{label:e.target.value})}),_jsx(Dropdown,{value:e.y_axis??"left",options:Y_AXES,onChange:e=>_(t,{y_axis:e.value})}),_jsx(Button,{icon:"pi pi-trash",className:"p-button-text p-button-danger p-button-sm",onClick:()=>(e=>{c({...o,y:o.y.filter((t,l)=>l!==e)})})(t),"aria-label":"Remove series"})]},t)),_jsx("datalist",{id:"known-keys",children:i.map(e=>_jsx("option",{value:e},e))}),_jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem",marginTop:"1rem"},children:[_jsx(Button,{label:"Cancel",className:"p-button-text",onClick:l}),_jsx(Button,{label:"Save",onClick:()=>{const e=r.trim()?n.includes(r)&&r!==t?.viewId?`A view named "${r}" already exists.`:null:"View ID is required.";e?m(e):a(r,o)}})]})]})};
@@ -0,0 +1,8 @@
1
+ import type { TestMethod, FieldArrayKey } from '../types';
2
+ export interface FieldArrayEditorProps {
3
+ arrayKey: FieldArrayKey;
4
+ method: TestMethod;
5
+ onChange: (next: TestMethod) => void;
6
+ }
7
+ export declare const FieldArrayEditor: React.FC<FieldArrayEditorProps>;
8
+ //# sourceMappingURL=FieldArrayEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldArrayEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/FieldArrayEditor.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAa,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAgBrE,MAAM,WAAW,qBAAqB;IAClC,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACxC;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAsG5D,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useState}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Button}from"primereact/button";import{FormSection}from"../../forms/FormSection";import{TestFieldDialog}from"./TestFieldDialog";const TITLES={project_fields:"Project Fields",config_fields:"Config Fields",cycle_fields:"Cycle Fields",results_fields:"Results Fields"},DESCRIPTIONS={project_fields:"System-level fields (operator, station). Filled by the HMI but not cycled.",config_fields:"Operator-input config (speeds, loads). Snapshotted into test.json on start.",cycle_fields:"Per-cycle capture. One row appended to cycles.jsonl per cycle.",results_fields:"Post-test summary (min/max/avg, pass/fail). Written once at finish."};export const FieldArrayEditor=({arrayKey:e,method:t,onChange:i})=>{const o=t[e]??[],[l,s]=useState(!1),[n,r]=useState(null),a=(l,s)=>{const n=l+s;if(n<0||n>=o.length)return;const r=[...o];[r[l],r[n]]=[r[n],r[l]],i({...t,[e]:r})};return _jsxs(_Fragment,{children:[_jsx(FormSection,{title:TITLES[e],description:DESCRIPTIONS[e],actions:_jsx(Button,{label:"Add field",icon:"pi pi-plus",size:"small",onClick:()=>{r(null),s(!0)}}),children:_jsxs(DataTable,{value:o,dataKey:"name",emptyMessage:"No fields defined.",children:[_jsx(Column,{field:"name",header:"Name"}),_jsx(Column,{field:"type",header:"Type",style:{width:"6rem"}}),_jsx(Column,{field:"units",header:"Units",style:{width:"6rem"}}),_jsx(Column,{header:"Req",body:e=>e.required?"✓":"",style:{width:"4rem"}}),_jsx(Column,{field:"label",header:"Label"}),_jsx(Column,{header:"",body:(l,n)=>_jsxs("div",{style:{display:"flex",gap:"0.25rem"},children:[_jsx(Button,{icon:"pi pi-arrow-up",className:"p-button-text p-button-sm",disabled:0===n.rowIndex,onClick:()=>a(n.rowIndex,-1),"aria-label":"Move up"}),_jsx(Button,{icon:"pi pi-arrow-down",className:"p-button-text p-button-sm",disabled:n.rowIndex===o.length-1,onClick:()=>a(n.rowIndex,1),"aria-label":"Move down"}),_jsx(Button,{icon:"pi pi-pencil",className:"p-button-text p-button-sm",onClick:()=>{return e=n.rowIndex,r(e),void s(!0);var e},"aria-label":"Edit"}),_jsx(Button,{icon:"pi pi-trash",className:"p-button-text p-button-danger p-button-sm",onClick:()=>(l=>{const s=o[l];if(!s)return;if(!window.confirm(`Remove field "${s.name}"?`))return;const n=o.filter((e,t)=>t!==l);i({...t,[e]:n})})(n.rowIndex),"aria-label":"Remove"})]}),style:{width:"10rem"}})]})}),_jsx(TestFieldDialog,{visible:l,initial:null!==n?o[n]??null:null,siblingNames:o.map(e=>e.name),onCancel:()=>s(!1),onSave:l=>{const r=[...o];null===n?r.push(l):r[n]=l,i({...t,[e]:r}),s(!1)}})]})};
@@ -0,0 +1,7 @@
1
+ import type { TestMethod } from '../types';
2
+ export interface IdentitySectionProps {
3
+ method: TestMethod;
4
+ onChange: (next: TestMethod) => void;
5
+ }
6
+ export declare const IdentitySection: React.FC<IdentitySectionProps>;
7
+ //# sourceMappingURL=IdentitySection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IdentitySection.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/IdentitySection.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,oBAAoB;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACxC;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAwB1D,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import{InputText}from"primereact/inputtext";import{InputTextarea}from"primereact/inputtextarea";import{FormSection}from"../../forms/FormSection";import{FormRow}from"../../forms/FormRow";export const IdentitySection=({method:e,onChange:t})=>_jsxs(FormSection,{title:"Identity",description:"Display label and operator-facing description for this method.",children:[_jsx(FormRow,{label:"Label",hint:"Pretty name shown in the Test Method picker.",children:_jsx(InputText,{value:e.label??"",onChange:o=>t({...e,label:o.target.value})})}),_jsx(FormRow,{label:"Description",hint:"Long-form guidance shown beneath the picker when this method is selected.",children:_jsx(InputTextarea,{rows:3,value:e.description??"",onChange:o=>t({...e,description:o.target.value})})})]});
@@ -0,0 +1,20 @@
1
+ /**
2
+ * MethodFormEditor — tabbed shell that composes the per-section subforms
3
+ * for a single TestMethod. The JSON tab is always available as an escape
4
+ * hatch for power users.
5
+ *
6
+ * Owns the local working copy of the method. Hands the changed copy back
7
+ * to the parent via onChange — the parent decides when to call put_method.
8
+ */
9
+ import type { TestMethod } from '../types';
10
+ export interface MethodFormEditorProps {
11
+ methodId: string;
12
+ method: TestMethod;
13
+ /** Called on Apply with the working copy — parent forwards to put_method. */
14
+ onApply: (next: TestMethod) => Promise<void> | void;
15
+ /** AMS asset types — passed through to AssetRefsEditor for the dropdown. */
16
+ knownAssetTypes?: string[];
17
+ busy?: boolean;
18
+ }
19
+ export declare const MethodFormEditor: React.FC<MethodFormEditorProps>;
20
+ //# sourceMappingURL=MethodFormEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MethodFormEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/MethodFormEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAaH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,qBAAqB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,6EAA6E;IAC7E,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACpD,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA+I5D,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import{useEffect,useMemo,useState}from"react";import{TabView,TabPanel}from"primereact/tabview";import{Button}from"primereact/button";import{CodeEditor}from"../../CodeEditor";import{IdentitySection}from"./IdentitySection";import{FieldArrayEditor}from"./FieldArrayEditor";import{ViewsEditor}from"./ViewsEditor";import{RawDataEditor}from"./RawDataEditor";import{AssetRefsEditor}from"./AssetRefsEditor";import{AnalysisEditor}from"./AnalysisEditor";import{validateMethod}from"../validation";export const MethodFormEditor=({methodId:e,method:t,onApply:r,knownAssetTypes:i=[],busy:s})=>{const[o,a]=useState(t),[n,d]=useState(""),[l,h]=useState(null),[m,c]=useState(0);useEffect(()=>{a(t),d(JSON.stringify(t,null,2)),h(null)},[t,e]);const y=useMemo(()=>{try{return JSON.stringify(o)!==JSON.stringify(t)}catch{return!0}},[o,t]),f=useMemo(()=>validateMethod(e,o),[o,e]),x=e=>{a(e),d(JSON.stringify(e,null,2))};return _jsxs("div",{style:{display:"flex",flexDirection:"column",height:"100%",minHeight:0},children:[_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"0.5rem 1rem",borderBottom:"1px solid var(--surface-d, #e2e8f0)",background:"var(--surface-b, #f8fafc)"},children:[_jsxs("strong",{children:[e,y&&_jsx("span",{style:{marginLeft:"0.5rem",color:"#ea580c"},children:"•"}),f.length>0&&_jsxs("span",{title:f.map(e=>e.message).join("\n"),style:{marginLeft:"0.5rem",background:"#fef2f2",color:"#991b1b",padding:"0.1rem 0.5rem",borderRadius:4,fontSize:"0.75rem",fontWeight:600,cursor:"help"},children:[f.length," issue",1===f.length?"":"s"]})]}),_jsx("div",{style:{display:"flex",gap:"0.5rem"},children:_jsx(Button,{label:"Apply",icon:"pi pi-check",disabled:s||!y||!!l||f.length>0,onClick:async()=>{l||await r(o)}})})]}),_jsx("div",{style:{flex:1,minHeight:0,overflow:"auto"},children:_jsxs(TabView,{activeIndex:m,onTabChange:e=>c(e.index),children:[_jsx(TabPanel,{header:"Identity",children:_jsx(IdentitySection,{method:o,onChange:x})}),_jsxs(TabPanel,{header:"Fields",children:[_jsx(FieldArrayEditor,{arrayKey:"project_fields",method:o,onChange:x}),_jsx(FieldArrayEditor,{arrayKey:"config_fields",method:o,onChange:x}),_jsx(FieldArrayEditor,{arrayKey:"cycle_fields",method:o,onChange:x}),_jsx(FieldArrayEditor,{arrayKey:"results_fields",method:o,onChange:x})]}),_jsx(TabPanel,{header:"Views",children:_jsx(ViewsEditor,{method:o,onChange:x})}),_jsx(TabPanel,{header:"Raw Data",children:_jsx(RawDataEditor,{method:o,onChange:x})}),_jsx(TabPanel,{header:"Assets",children:_jsx(AssetRefsEditor,{method:o,onChange:x,knownAssetTypes:i})}),_jsx(TabPanel,{header:"Analysis",children:_jsx(AnalysisEditor,{method:o,onChange:x})}),_jsxs(TabPanel,{header:"JSON",children:[l&&_jsx("div",{style:{color:"#dc2626",marginBottom:"0.5rem",fontFamily:"monospace"},children:l}),_jsx("div",{style:{height:"60vh"},children:_jsx(CodeEditor,{code:n,language:"json",theme:"vs-dark",readOnly:!1,cursorStyle:"line",onCodeChanged:e=>{const t=e??"";d(t);try{const e=JSON.parse(t);a(e),h(null)}catch(e){h(String(e?.message??e))}}})})]})]})})]})};
@@ -0,0 +1,7 @@
1
+ import type { TestMethod } from '../types';
2
+ export interface RawDataEditorProps {
3
+ method: TestMethod;
4
+ onChange: (next: TestMethod) => void;
5
+ }
6
+ export declare const RawDataEditor: React.FC<RawDataEditorProps>;
7
+ //# sourceMappingURL=RawDataEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RawDataEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/RawDataEditor.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAA2B,UAAU,EAAE,MAAM,UAAU,CAAC;AAEpE,MAAM,WAAW,kBAAkB;IAC/B,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACxC;AAID,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAsGtD,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{InputText}from"primereact/inputtext";import{Button}from"primereact/button";import{Checkbox}from"primereact/checkbox";import{FormSection}from"../../forms/FormSection";import{FormRow}from"../../forms/FormRow";const emptyRawData=()=>({blob_name:"trace",columns:{}});export const RawDataEditor=({method:e,onChange:t})=>{const n=e.raw_data,o=!!n,r=n=>{t({...e,raw_data:n})},a=(e,t)=>{n&&r({...n,columns:{...n.columns,[e]:t}})};return _jsxs(FormSection,{title:"Raw Data",description:"Columnar blob shape captured per cycle under raw_data/. Required only when the method records DAQ traces.",actions:_jsx(Checkbox,{checked:o,onChange:e=>r(e.checked?emptyRawData():null)}),children:[!o&&_jsx("small",{children:"Raw data capture disabled. Tick the box above to enable."}),o&&n&&_jsxs(_Fragment,{children:[_jsx(FormRow,{label:"Blob name",required:!0,hint:"Base filename under raw_data/, no extension.",children:_jsx(InputText,{value:n.blob_name,onChange:e=>r({...n,blob_name:e.target.value})})}),_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginTop:"0.75rem"},children:[_jsx("strong",{children:"Columns"}),_jsx(Button,{label:"Add column",icon:"pi pi-plus",size:"small",onClick:()=>{if(!n)return;let e="column",t=1;for(;n.columns[`${e}${t}`];)t++;r({...n,columns:{...n.columns,[`${e}${t}`]:{source:"time"}}})}})]}),0===Object.keys(n.columns).length&&_jsx("small",{children:"No columns defined."}),Object.entries(n.columns).map(([e,t])=>_jsxs("div",{style:{display:"grid",gridTemplateColumns:"1fr 1.5fr 1.5fr 2rem",gap:"0.5rem",marginTop:"0.25rem",alignItems:"center"},children:[_jsx(InputText,{placeholder:"column name",defaultValue:e,onBlur:t=>((e,t)=>{if(!n||!t.trim()||t===e)return;if(n.columns[t])return;const o={...n.columns};o[t]=o[e],delete o[e],r({...n,columns:o})})(e,t.target.value.trim())}),_jsx(InputText,{placeholder:"source (time / ni.<daq>.channels.<name> / derived)",value:t.source,onChange:n=>a(e,{...t,source:n.target.value})}),_jsx(InputText,{placeholder:"formula (only when source=derived)",value:t.formula??"",onChange:n=>a(e,{...t,formula:n.target.value||void 0}),disabled:"derived"!==t.source}),_jsx(Button,{icon:"pi pi-trash",className:"p-button-text p-button-danger p-button-sm",onClick:()=>(e=>{if(!n)return;if(!window.confirm(`Remove column "${e}"?`))return;const t={...n.columns};delete t[e],r({...n,columns:t})})(e),"aria-label":"Remove column"})]},e))]})]})};
@@ -0,0 +1,22 @@
1
+ /**
2
+ * SaveDiffDialog — shows what's about to land on disk before save_config.
3
+ *
4
+ * Fetches a fresh disk-state via `tis.list_schemas` (the existing read
5
+ * endpoint that bypasses staging) and diffs against the current staged
6
+ * methods. Operator confirms or cancels.
7
+ *
8
+ * The diff is intentionally simple: added / removed / modified method IDs,
9
+ * plus a JSON before/after for each modified method. Adding a full
10
+ * line-level differ would mean another dep — not worth it for an HMI.
11
+ */
12
+ import type { TisIpcInvoker } from '../../../hooks/useTisConfig';
13
+ import type { TestMethod } from '../types';
14
+ export interface SaveDiffDialogProps {
15
+ visible: boolean;
16
+ staged: Record<string, TestMethod>;
17
+ invoker: TisIpcInvoker;
18
+ onConfirm: () => Promise<void> | void;
19
+ onCancel: () => void;
20
+ }
21
+ export declare const SaveDiffDialog: React.FC<SaveDiffDialogProps>;
22
+ //# sourceMappingURL=SaveDiffDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SaveDiffDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/SaveDiffDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAQD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAgGxD,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useEffect,useState}from"react";import{Dialog}from"primereact/dialog";import{Button}from"primereact/button";export const SaveDiffDialog=({visible:e,staged:i,invoker:r,onConfirm:t,onCancel:s})=>{const[d,o]=useState(null),[n,l]=useState(null),[a,c]=useState(!1);useEffect(()=>{if(!e)return;let t=!1;return(async()=>{l(null);try{const e=await r("tis.list_schemas",{});if(t)return;if(!e.success)return void l(e.error_message??"tis.list_schemas failed");const s=e.data?.test_methods??{};o(buildDiff(s,i))}catch(e){l(String(e?.message??e))}})(),()=>{t=!0}},[e,i,r]);return _jsxs(Dialog,{header:"Save Test Methods to Disk",visible:e,onHide:s,style:{width:"60rem",maxWidth:"90vw"},children:[n&&_jsx("div",{style:{color:"#dc2626",marginBottom:"0.5rem"},children:n}),!d&&!n&&_jsx("small",{children:"Computing diff…"}),d&&_jsxs("div",{children:[_jsxs("p",{children:["About to overwrite ",_jsx("code",{children:"project.json"}),". A backup of the previous file will be written to"," ",_jsx("code",{children:"project.json.bak"}),"."]}),0===d.added.length&&0===d.removed.length&&0===d.modified.length&&_jsx("p",{children:_jsx("em",{children:"No changes detected."})}),d.added.length>0&&_jsxs("p",{children:[_jsx("strong",{children:"Added:"})," ",d.added.join(", ")]}),d.removed.length>0&&_jsxs("p",{children:[_jsx("strong",{children:"Removed:"})," ",d.removed.join(", ")]}),d.modified.length>0&&_jsxs(_Fragment,{children:[_jsxs("p",{children:[_jsx("strong",{children:"Modified:"})," ",d.modified.map(e=>e.id).join(", ")]}),_jsxs("details",{children:[_jsx("summary",{children:"Show before / after"}),d.modified.map(e=>_jsxs("div",{style:{marginTop:"0.75rem"},children:[_jsx("strong",{children:e.id}),_jsxs("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"0.5rem",marginTop:"0.25rem"},children:[_jsx("pre",{style:preStyle("before"),children:JSON.stringify(e.before,null,2)}),_jsx("pre",{style:preStyle("after"),children:JSON.stringify(e.after,null,2)})]})]},e.id))]})]})]}),_jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem",marginTop:"1rem"},children:[_jsx(Button,{label:"Cancel",className:"p-button-text",onClick:s,disabled:a}),_jsx(Button,{label:"Save",icon:"pi pi-save",disabled:a||!d,onClick:async()=>{c(!0);try{await t()}finally{c(!1)}}})]})]})};function preStyle(e){return{background:"before"===e?"#fef2f2":"#f0fdf4",color:"#0f172a",padding:"0.5rem",fontSize:"0.75rem",margin:0,maxHeight:"20rem",overflow:"auto",border:"1px solid "+("before"===e?"#fecaca":"#bbf7d0"),borderRadius:4}}function buildDiff(e,i){const r={added:[],removed:[],modified:[]},t=new Set([...Object.keys(e),...Object.keys(i)]);for(const s of t){const t=e[s],d=i[s];void 0===t&&void 0!==d?r.added.push(s):void 0!==t&&void 0===d?r.removed.push(s):void 0!==t&&void 0!==d&&JSON.stringify(t)!==JSON.stringify(d)&&r.modified.push({id:s,before:t,after:d})}return r}
@@ -0,0 +1,11 @@
1
+ import type { TestField } from '../types';
2
+ export interface TestFieldDialogProps {
3
+ visible: boolean;
4
+ initial: TestField | null;
5
+ onCancel: () => void;
6
+ onSave: (field: TestField) => void;
7
+ /** Names already in use within the same array — for uniqueness validation. */
8
+ siblingNames: string[];
9
+ }
10
+ export declare const TestFieldDialog: React.FC<TestFieldDialogProps>;
11
+ //# sourceMappingURL=TestFieldDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestFieldDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/TestFieldDialog.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAa1C,MAAM,WAAW,oBAAoB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACnC,8EAA8E;IAC9E,YAAY,EAAE,MAAM,EAAE,CAAC;CAC1B;AAID,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAoG1D,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import{useEffect,useState}from"react";import{Dialog}from"primereact/dialog";import{Button}from"primereact/button";import{InputText}from"primereact/inputtext";import{InputTextarea}from"primereact/inputtextarea";import{Dropdown}from"primereact/dropdown";import{Checkbox}from"primereact/checkbox";import{InputNumber}from"primereact/inputnumber";import{FormRow}from"../../forms/FormRow";const FIELD_TYPES=[{label:"string",value:"string"},{label:"i32",value:"i32"},{label:"i64",value:"i64"},{label:"u32",value:"u32"},{label:"u64",value:"u64"},{label:"f32",value:"f32"},{label:"f64",value:"f64"},{label:"bool",value:"bool"}],blank={name:"",type:"f32"};export const TestFieldDialog=({visible:e,initial:a,onCancel:t,onSave:l,siblingNames:r})=>{const[i,o]=useState(blank),[n,s]=useState(null);useEffect(()=>{e&&(o(a?{...a}:{...blank}),s(null))},[e,a]);return _jsxs(Dialog,{header:a?`Edit field: ${a.name}`:"New field",visible:e,onHide:t,style:{width:"36rem"},children:[n&&_jsx("div",{style:{color:"#dc2626",marginBottom:"0.75rem"},children:n}),_jsx(FormRow,{label:"Name",required:!0,hint:"Wire-format key. Also the column name in CSV exports.",children:_jsx(InputText,{value:i.name,onChange:e=>o({...i,name:e.target.value})})}),_jsx(FormRow,{label:"Type",required:!0,children:_jsx(Dropdown,{value:i.type,options:FIELD_TYPES,onChange:e=>o({...i,type:e.value}),editable:!0})}),_jsx(FormRow,{label:"Units",hint:"Display label, appended to form labels (e.g. m/s).",children:_jsx(InputText,{value:i.units??"",onChange:e=>o({...i,units:e.target.value||void 0})})}),_jsx(FormRow,{label:"Label",hint:"Pretty form label. Falls back to name when empty.",children:_jsx(InputText,{value:i.label??"",onChange:e=>o({...i,label:e.target.value||void 0})})}),_jsx(FormRow,{label:"Required",children:_jsx(Checkbox,{checked:!!i.required,onChange:e=>o({...i,required:!!e.checked})})}),_jsx(FormRow,{label:"Source",hint:"Optional gm.* variable to bind this field to.",children:_jsx(InputText,{value:i.source??"",onChange:e=>o({...i,source:e.target.value||void 0}),placeholder:"gm.<variable_name>"})}),_jsx(FormRow,{label:"Scale",hint:"display = raw × scale. Leave blank for 1.0 (no conversion).",children:_jsx(InputNumber,{value:i.scale??null,onValueChange:e=>o({...i,scale:"number"==typeof e.value?e.value:void 0}),mode:"decimal",minFractionDigits:0,maxFractionDigits:9})}),_jsx(FormRow,{label:"Description",hint:"Hover tooltip in the form.",children:_jsx(InputTextarea,{rows:2,value:i.description??"",onChange:e=>o({...i,description:e.target.value||void 0})})}),_jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem",marginTop:"1rem"},children:[_jsx(Button,{label:"Cancel",className:"p-button-text",onClick:t}),_jsx(Button,{label:"Save",onClick:()=>{const e=(t=i).name.trim()?/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(t.name)?r.some(e=>e===t.name&&e!==a?.name)?`A field named "${t.name}" already exists in this array.`:null:"Name must be a valid identifier (letters, digits, underscore; cannot start with a digit).":"Field name is required.";var t;e?s(e):l(i)}})]})]})};
@@ -0,0 +1,7 @@
1
+ import type { TestMethod } from '../types';
2
+ export interface ViewsEditorProps {
3
+ method: TestMethod;
4
+ onChange: (next: TestMethod) => void;
5
+ }
6
+ export declare const ViewsEditor: React.FC<ViewsEditorProps>;
7
+ //# sourceMappingURL=ViewsEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ViewsEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/ViewsEditor.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAwB,UAAU,EAAE,MAAM,UAAU,CAAC;AAEjE,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACxC;AAQD,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAiFlD,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useState}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Button}from"primereact/button";import{FormSection}from"../../forms/FormSection";import{ChartViewDialog}from"./ChartViewDialog";export const ViewsEditor=({method:e,onChange:t})=>{const i=e.views??{},a=Object.entries(i).map(([e,t])=>({id:e,title:t.title??"",type:t.type??""})),[s,n]=useState(!1),[o,r]=useState(null),l=[...(e.project_fields??[]).map(e=>e.name),...(e.config_fields??[]).map(e=>e.name),...(e.cycle_fields??[]).map(e=>e.name),...(e.results_fields??[]).map(e=>e.name),...Object.keys(e.raw_data?.columns??{})];return _jsxs(_Fragment,{children:[_jsx(FormSection,{title:"Views",description:"Named chart definitions rendered by <TestDataView> (cycle_scatter) and <TestRawDataView> (raw_trace).",actions:_jsx(Button,{label:"Add view",icon:"pi pi-plus",size:"small",onClick:()=>{r(null),n(!0)}}),children:_jsxs(DataTable,{value:a,dataKey:"id",emptyMessage:"No views defined.",children:[_jsx(Column,{field:"id",header:"View ID"}),_jsx(Column,{field:"title",header:"Title"}),_jsx(Column,{field:"type",header:"Type",style:{width:"8rem"}}),_jsx(Column,{header:"",body:a=>_jsxs("div",{style:{display:"flex",gap:"0.25rem"},children:[_jsx(Button,{icon:"pi pi-pencil",className:"p-button-text p-button-sm",onClick:()=>{return e=a.id,r(e),void n(!0);var e},"aria-label":"Edit"}),_jsx(Button,{icon:"pi pi-trash",className:"p-button-text p-button-danger p-button-sm",onClick:()=>(a=>{if(!window.confirm(`Remove view "${a}"?`))return;const s={...i};delete s[a],t({...e,views:s})})(a.id),"aria-label":"Remove"})]}),style:{width:"6rem"}})]})}),_jsx(ChartViewDialog,{visible:s,initial:o?{viewId:o,view:i[o]}:null,knownKeys:l,siblingIds:Object.keys(i),onCancel:()=>n(!1),onSave:(a,s)=>{const r={...i};o&&o!==a&&delete r[o],r[a]=s,t({...e,views:r}),n(!1)}})]})};
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Client-side mirror of the autocore-server TestMethod schema.
3
+ * Source of truth is autocore-server/src/project.rs.
4
+ *
5
+ * Kept loose — `unknown` for default values and pass-through fields — so
6
+ * unknown server-side extensions don't trip the editor at parse time.
7
+ */
8
+ export type FieldType = 'string' | 'i32' | 'i64' | 'u32' | 'u64' | 'f32' | 'f64' | 'bool';
9
+ export interface TestField {
10
+ name: string;
11
+ type: FieldType | string;
12
+ units?: string;
13
+ required?: boolean;
14
+ source?: string;
15
+ label?: string;
16
+ description?: string;
17
+ default?: unknown;
18
+ scale?: number;
19
+ }
20
+ export interface ChartAxis {
21
+ field?: string;
22
+ column?: string;
23
+ label?: string;
24
+ }
25
+ export interface ChartSeries {
26
+ field?: string;
27
+ column?: string;
28
+ label?: string;
29
+ y_axis?: 'left' | 'right';
30
+ }
31
+ export type ChartViewType = 'cycle_scatter' | 'raw_trace';
32
+ export interface ChartView {
33
+ title?: string;
34
+ type: ChartViewType | string;
35
+ x: ChartAxis;
36
+ y: ChartSeries[];
37
+ }
38
+ export type RawColumnSource = 'time' | 'derived' | string;
39
+ export interface RawColumn {
40
+ source: RawColumnSource;
41
+ formula?: string;
42
+ }
43
+ export interface RawDataShape {
44
+ blob_name: string;
45
+ columns: Record<string, RawColumn>;
46
+ units?: Record<string, string>;
47
+ }
48
+ export type AssetRefSelect = 'by_location' | 'by_id_field';
49
+ export type CalibrationPolicy = 'ignore' | 'warn' | 'require';
50
+ export interface AssetRef {
51
+ field: string;
52
+ asset_type: string;
53
+ select: AssetRefSelect | string;
54
+ location?: string;
55
+ from?: string;
56
+ calibration_required?: CalibrationPolicy | string;
57
+ label?: string;
58
+ description?: string;
59
+ }
60
+ export interface AnalysisShape {
61
+ script: string;
62
+ function: string;
63
+ }
64
+ export interface TestMethod {
65
+ label?: string;
66
+ description?: string;
67
+ project_fields?: TestField[];
68
+ config_fields?: TestField[];
69
+ cycle_fields?: TestField[];
70
+ results_fields?: TestField[];
71
+ raw_data?: RawDataShape | null;
72
+ views?: Record<string, ChartView>;
73
+ asset_refs?: AssetRef[];
74
+ analysis?: AnalysisShape | null;
75
+ [key: string]: unknown;
76
+ }
77
+ export type FieldArrayKey = 'project_fields' | 'config_fields' | 'cycle_fields' | 'results_fields';
78
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/tis-editor/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,SAAS,GACf,QAAQ,GACR,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAC7B,KAAK,GAAG,KAAK,GACb,MAAM,CAAC;AAEb,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC7B;AAED,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG,WAAW,CAAC;AAE1D,MAAM,WAAW,SAAS;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,aAAa,GAAG,MAAM,CAAC;IAC7B,CAAC,EAAE,SAAS,CAAC;IACb,CAAC,EAAE,WAAW,EAAE,CAAC;CACpB;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,SAAS;IACtB,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,aAAa,CAAC;AAC3D,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAE9D,MAAM,WAAW,QAAQ;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,cAAc,GAAG,MAAM,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oBAAoB,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,SAAS,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC;IAC5B,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC;IAC3B,cAAc,CAAC,EAAE,SAAS,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,MAAM,MAAM,aAAa,GAAG,gBAAgB,GAAG,eAAe,GAAG,cAAc,GAAG,gBAAgB,CAAC"}
@@ -0,0 +1 @@
1
+ export{};
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Client-side validation for TestMethod. Mirrors the cross-field checks
3
+ * in autocore-server/src/tis_servelet.rs `validate_method` so the operator
4
+ * sees the same errors instantly in the form, not only after Apply.
5
+ *
6
+ * Returns a flat array of error messages. Empty array = clean.
7
+ */
8
+ import type { TestMethod } from './types';
9
+ export interface ValidationError {
10
+ /** Dotted path: "<arrayKey>.<index>.<sub>" or "views.<viewId>.x" etc. */
11
+ path: string;
12
+ message: string;
13
+ }
14
+ export declare function validateMethod(methodId: string, m: TestMethod): ValidationError[];
15
+ /**
16
+ * Validate a whole methods map. Returns errors grouped by methodId.
17
+ * Useful for the master-detail sidebar (display an error count per row).
18
+ */
19
+ export declare function validateMethods(methods: Record<string, TestMethod>): Record<string, ValidationError[]>;
20
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/components/tis-editor/validation.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACG,UAAU,EACxB,MAAM,SAAS,CAAC;AAIjB,MAAM,WAAW,eAAe;IAC5B,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,GAAG,eAAe,EAAE,CAsEjF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC,CAOtG"}
@@ -0,0 +1 @@
1
+ const FIELD_ARRAY_KEYS=["project_fields","config_fields","cycle_fields","results_fields"];export function validateMethod(e,a){const t=[];for(const n of FIELD_ARRAY_KEYS){const o=a[n]??[],s=new Set;o.forEach((a,o)=>{a.name&&""!==a.name.trim()?s.has(a.name)?t.push({path:`${n}.${o}.name`,message:`${e}.${n}: duplicate field name "${a.name}"`}):s.add(a.name):t.push({path:`${n}.${o}.name`,message:`${e}.${n}: empty field name`})})}const n=new Set;for(const e of FIELD_ARRAY_KEYS){(a[e]??[]).forEach(e=>e.name&&n.add(e.name))}const o=new Set(Object.keys(a.raw_data?.columns??{})),s=e=>e.field&&e.field.trim()?{key:e.field,ok:n.has(e.field)}:e.column&&e.column.trim()?{key:e.column,ok:o.has(e.column)}:null,c=a.views??{};for(const[a,n]of Object.entries(c)){if(n?.x){const o=s(n.x);o&&!o.ok&&t.push({path:`views.${a}.x`,message:`${e}.views.${a}: x.field/column "${o.key}" does not match any field or raw_data column`})}(n?.y??[]).forEach((n,o)=>{const c=s(n);c&&!c.ok&&t.push({path:`views.${a}.y.${o}`,message:`${e}.views.${a}.y[${o}]: field/column "${c.key}" does not match any field or raw_data column`})})}const m=a.raw_data;return m&&(m.blob_name&&""!==String(m.blob_name).trim()||t.push({path:"raw_data.blob_name",message:`${e}.raw_data: blob_name is empty`})),t}export function validateMethods(e){const a={};for(const[t,n]of Object.entries(e)){const e=validateMethod(t,n);e.length>0&&(a[t]=e)}return a}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * useAmsAssetTypes — fetch the AMS asset-type catalog.
3
+ *
4
+ * Used by the TIS editor's AssetRefsEditor to populate the asset_type
5
+ * dropdown with real values instead of free-form text. Falls back to
6
+ * an empty array on error (the dropdown degrades gracefully to a text
7
+ * field).
8
+ *
9
+ * Wraps `ams.list_schemas`, which returns the merged catalog of
10
+ * built-in + project-declared asset types. Accepts an `invoker` override
11
+ * for the playground / tests.
12
+ */
13
+ import type { TisIpcInvoker } from './useTisConfig';
14
+ export interface UseAmsAssetTypesResult {
15
+ types: string[];
16
+ loading: boolean;
17
+ error: string | null;
18
+ refresh: () => Promise<void>;
19
+ }
20
+ export declare function useAmsAssetTypes(options?: {
21
+ invoker?: TisIpcInvoker;
22
+ }): UseAmsAssetTypesResult;
23
+ //# sourceMappingURL=useAmsAssetTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAmsAssetTypes.d.ts","sourceRoot":"","sources":["../../src/hooks/useAmsAssetTypes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,MAAM,WAAW,sBAAsB;IACnC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAED,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,aAAa,CAAA;CAAE,GAAG,sBAAsB,CA4C9F"}
@@ -0,0 +1 @@
1
+ import{useCallback,useContext,useEffect,useState}from"react";import{EventEmitterContext}from"../core/EventEmitterContext";import{MessageType}from"../hub/CommandMessage";export function useAmsAssetTypes(e){const t=useContext(EventEmitterContext),s=e?.invoker??(async(e,s)=>await t.invoke(e,MessageType.Request,s)),[a,o]=useState([]),[r,n]=useState(!0),[c,m]=useState(null),u=useCallback(async()=>{n(!0);try{const e=await s("ams.list_schemas",{});if(!e.success)return m(e.error_message??"ams.list_schemas failed"),void o([]);const t=e.data??{},a=t.asset_types&&"object"==typeof t.asset_types?t.asset_types:t.schemas&&"object"==typeof t.schemas?t.schemas:null;o(a?Object.keys(a):[]),m(null)}catch(e){m(String(e?.message??e)),o([])}finally{n(!1)}},[s]);return useEffect(()=>{u()},[u]),{types:a,loading:r,error:c,refresh:u}}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * useTisConfig — load and edit the TIS test_methods schema.
3
+ *
4
+ * Thin wrapper around the `tis.show_config` / `tis.put_method` /
5
+ * `tis.remove_method` / `tis.save_config` / `tis.discard_config_changes`
6
+ * IPC surface. Mirrors the server's staged-then-saved model: edits land in
7
+ * the server's in-memory copy (dirty = true) until `save()` persists them
8
+ * to project.json.
9
+ *
10
+ * Today the server hosts exactly one project (the one configured at
11
+ * startup), so `projectId` is informational; the hook still threads it
12
+ * through every call so the wire contract matches a future multi-project
13
+ * world without a rewrite.
14
+ *
15
+ * Pass an `invoker` override to mock the IPC layer (used by the playground
16
+ * demo and by tests). When omitted, the hook reads `EventEmitterContext`.
17
+ */
18
+ /** Minimal subset of a TestMethod we model client-side. Treat as opaque —
19
+ * the structural integrity of each method is the server's responsibility. */
20
+ export type TestMethod = Record<string, unknown>;
21
+ export interface TisConfig {
22
+ projectId: string;
23
+ methods: Record<string, TestMethod>;
24
+ dirty: boolean;
25
+ defaultMethodId: string;
26
+ }
27
+ /** IPC invoker contract: `topic` is e.g. "tis.show_config", payload is the
28
+ * request data. Returns an object with at least { success, data?, error_message? }. */
29
+ export interface TisIpcInvoker {
30
+ (topic: string, payload: object): Promise<{
31
+ success: boolean;
32
+ data?: any;
33
+ error_message?: string;
34
+ }>;
35
+ }
36
+ export interface UseTisConfigResult {
37
+ config: TisConfig | null;
38
+ loading: boolean;
39
+ /** Last server-reported error from a mutation. Cleared on next successful call. */
40
+ error: string | null;
41
+ refresh: () => Promise<void>;
42
+ putMethod: (methodId: string, method: TestMethod) => Promise<void>;
43
+ removeMethod: (methodId: string) => Promise<void>;
44
+ save: () => Promise<void>;
45
+ revert: () => Promise<void>;
46
+ }
47
+ export interface UseTisConfigOptions {
48
+ invoker?: TisIpcInvoker;
49
+ }
50
+ export declare function useTisConfig(projectId: string, options?: UseTisConfigOptions): UseTisConfigResult;
51
+ //# sourceMappingURL=useTisConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTisConfig.d.ts","sourceRoot":"","sources":["../../src/hooks/useTisConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH;8EAC8E;AAC9E,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,SAAS;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;CAC3B;AAED;wFACwF;AACxF,MAAM,WAAW,aAAa;IAC1B,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QACtC,OAAO,EAAE,OAAO,CAAC;QACjB,IAAI,CAAC,EAAE,GAAG,CAAC;QACX,aAAa,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC,CAAC;CACN;AAED,MAAM,WAAW,kBAAkB;IAC/B,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAChC,OAAO,CAAC,EAAE,aAAa,CAAC;CAC3B;AAED,wBAAgB,YAAY,CACxB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,mBAAmB,GAC9B,kBAAkB,CAqGpB"}
@@ -0,0 +1 @@
1
+ import{useCallback,useContext,useEffect,useState}from"react";import{EventEmitterContext}from"../core/EventEmitterContext";import{MessageType}from"../hub/CommandMessage";export function useTisConfig(e,t){const s=useContext(EventEmitterContext),o=t?.invoker??(async(e,t)=>await s.invoke(e,MessageType.Request,t)),[a,r]=useState(null),[i,n]=useState(!0),[c,d]=useState(null),l=useCallback(async()=>{n(!0);try{const t=await o("tis.show_config",{project_id:e});if(!t.success)return d(t.error_message??"tis.show_config failed"),void r(null);const s=t.data??{};r({projectId:e,methods:s.test_methods??{},dirty:!!s.dirty,defaultMethodId:"string"==typeof s.default_method_id?s.default_method_id:""}),d(null)}catch(e){d(String(e?.message??e)),r(null)}finally{n(!1)}},[e,o]);useEffect(()=>{l()},[l]);const u=useCallback(async(t,s)=>{const a=await o("tis.put_method",{project_id:e,method_id:t,method:s});if(!a.success){const e=a.error_message??"tis.put_method failed";throw d(e),new Error(e)}d(null),await l()},[o,e,l]),f=useCallback(async t=>{const s=await o("tis.remove_method",{project_id:e,method_id:t});if(!s.success){const e=s.error_message??"tis.remove_method failed";throw d(e),new Error(e)}d(null),await l()},[o,e,l]),m=useCallback(async()=>{const t=await o("tis.save_config",{project_id:e});if(!t.success){const e=t.error_message??"tis.save_config failed";throw d(e),new Error(e)}d(null),await l()},[o,e,l]),_=useCallback(async()=>{const t=await o("tis.discard_config_changes",{project_id:e});if(!t.success){const e=t.error_message??"tis.discard_config_changes failed";throw d(e),new Error(e)}d(null),await l()},[o,e,l]);return{config:a,loading:i,error:c,refresh:l,putMethod:u,removeMethod:f,save:m,revert:_}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adcops/autocore-react",
3
- "version": "3.3.84",
3
+ "version": "3.3.87",
4
4
  "description": "A React component library for industrial user interfaces.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -11,7 +11,10 @@
11
11
  "build-themes": "node ./tools/build-themes.cjs",
12
12
  "generate-docs": "typedoc",
13
13
  "build": "npm run clean && tsc && node ./tools/copy-distribution-files.cjs && npm run build-themes && npm run minify",
14
- "dev": "npm run clean && tsc && node ./tools/copy-distribution-files.cjs",
14
+ "build:dev": "npm run clean && tsc && node ./tools/copy-distribution-files.cjs",
15
+ "dev": "vite",
16
+ "playground": "vite",
17
+ "playground:build": "vite build",
15
18
  "prestart": "tsc",
16
19
  "publish-package": "npm run build && npm version patch && npm publish",
17
20
  "prepublishOnly": "npm run build",
@@ -58,6 +61,7 @@
58
61
  "numerable": "^0.3.15",
59
62
  "react-blockly": "^8.1.2",
60
63
  "react-chartjs-2": "^5.3.0",
64
+ "react-number-format": "^5.4.5",
61
65
  "react-simple-keyboard": "^3.8.120",
62
66
  "react-transition-group": "^4.4.5",
63
67
  "sass": "^1.92.1",
@@ -76,11 +80,13 @@
76
80
  "@types/react": "^18.2.15",
77
81
  "@types/react-dom": "^18.2.7",
78
82
  "@types/react-transition-group": "^4.4.12",
83
+ "@vitejs/plugin-react": "^6.0.2",
79
84
  "primereact": "^10.9.7",
80
85
  "rimraf": "^6.0.1",
81
86
  "terser": "^5.44.0",
82
87
  "typedoc": "^0.28.12",
83
- "typescript": "^5.9.2"
88
+ "typescript": "^5.9.2",
89
+ "vite": "^8.0.13"
84
90
  },
85
91
  "exports": {
86
92
  "./core/*": {
@@ -1,29 +1,26 @@
1
1
  /*
2
2
  * ValueInput sizing.
3
3
  *
4
- * Within a .p-inputgroup, PrimeReact gives .p-inputwrapper `flex: 1 1 auto`
5
- * so the InputNumber stretches to fill all available space. Override that
6
- * for our sized variants so only the input field is constrained — label,
7
- * accept/cancel buttons keep their natural size.
4
+ * NumericFormat with customInput=InputText renders a plain <input class="p-inputtext">
5
+ * (no .p-inputwrapper span like PrimeReact's InputNumber). In a .p-inputgroup the
6
+ * input defaults to flex: 1 1 auto; we override that so the field stays at a
7
+ * fixed width while the label addon and accept/cancel buttons keep their
8
+ * natural sizes.
8
9
  */
9
10
 
10
- .value-input-field.p-inputwrapper {
11
+ .value-input-field.p-inputtext {
11
12
  flex: 0 0 auto;
12
- }
13
-
14
- .value-input-field.p-inputwrapper .p-inputtext {
15
- width: 100%;
16
13
  box-sizing: border-box;
17
14
  }
18
15
 
19
- .value-input-field.size-small.p-inputwrapper {
16
+ .value-input-field.size-small.p-inputtext {
20
17
  width: 30mm;
21
18
  }
22
19
 
23
- .value-input-field.size-normal.p-inputwrapper {
20
+ .value-input-field.size-normal.p-inputtext {
24
21
  width: 55mm;
25
22
  }
26
23
 
27
- .value-input-field.size-large.p-inputwrapper {
24
+ .value-input-field.size-large.p-inputtext {
28
25
  width: 70mm;
29
26
  }