orcheo-studio 0.24.0 → 0.24.1

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 (65) hide show
  1. package/.bumpversion.cfg +1 -1
  2. package/dist/assets/{arc-DAv23sxp.js → arc-DhTjQsml.js} +1 -1
  3. package/dist/assets/{architectureDiagram-VXUJARFQ-D_gUzQPQ.js → architectureDiagram-VXUJARFQ-CGDm4iVL.js} +1 -1
  4. package/dist/assets/{blockDiagram-VD42YOAC-2_hx47qT.js → blockDiagram-VD42YOAC-B5xlwwyL.js} +1 -1
  5. package/dist/assets/{c4Diagram-YG6GDRKO-dehdDKxT.js → c4Diagram-YG6GDRKO-DtVXFxFX.js} +1 -1
  6. package/dist/assets/channel-CeR8j9Si.js +1 -0
  7. package/dist/assets/{chunk-4BX2VUAB-CtLOuP9F.js → chunk-4BX2VUAB-CA8vZ6Tb.js} +1 -1
  8. package/dist/assets/{chunk-55IACEB6-CwinXV4-.js → chunk-55IACEB6-CxNWLhGM.js} +1 -1
  9. package/dist/assets/{chunk-B4BG7PRW-BI7iPFRv.js → chunk-B4BG7PRW-BW8UEmwi.js} +1 -1
  10. package/dist/assets/{chunk-DI55MBZ5-CNYNrYjW.js → chunk-DI55MBZ5-_6YQcHqR.js} +1 -1
  11. package/dist/assets/{chunk-FMBD7UC4-BFVTHaU-.js → chunk-FMBD7UC4-L-9FXYi4.js} +1 -1
  12. package/dist/assets/{chunk-QN33PNHL-xWy7sSv1.js → chunk-QN33PNHL-DZtaWvmZ.js} +1 -1
  13. package/dist/assets/{chunk-QZHKN3VN-Ds_R1QaP.js → chunk-QZHKN3VN-B3c4-un7.js} +1 -1
  14. package/dist/assets/{chunk-TZMSLE5B-D8g2OOzb.js → chunk-TZMSLE5B-DmyAt3KD.js} +1 -1
  15. package/dist/assets/classDiagram-2ON5EDUG-B2tKUrqn.js +1 -0
  16. package/dist/assets/classDiagram-v2-WZHVMYZB-B2tKUrqn.js +1 -0
  17. package/dist/assets/clone-BN-HxSe-.js +1 -0
  18. package/dist/assets/{cose-bilkent-S5V4N54A-DnkMvL-f.js → cose-bilkent-S5V4N54A-r3dremCG.js} +1 -1
  19. package/dist/assets/{dagre-6UL2VRFP-dpKYJ1gN.js → dagre-6UL2VRFP-C6AizynV.js} +1 -1
  20. package/dist/assets/{diagram-PSM6KHXK-BnFoR8YQ.js → diagram-PSM6KHXK-CKTAJrFm.js} +1 -1
  21. package/dist/assets/{diagram-QEK2KX5R-Brg1AGVu.js → diagram-QEK2KX5R-B4BLbqZ6.js} +1 -1
  22. package/dist/assets/{diagram-S2PKOQOG-Dhmom7Q4.js → diagram-S2PKOQOG-CLy2S4hE.js} +1 -1
  23. package/dist/assets/{erDiagram-Q2GNP2WA-BTez8fDL.js → erDiagram-Q2GNP2WA-DlemVcfS.js} +1 -1
  24. package/dist/assets/{flowDiagram-NV44I4VS-CVXmtc0D.js → flowDiagram-NV44I4VS-CpBPPeuN.js} +1 -1
  25. package/dist/assets/{ganttDiagram-JELNMOA3-CHbw3AtF.js → ganttDiagram-JELNMOA3-BY4cJPdg.js} +1 -1
  26. package/dist/assets/{gitGraphDiagram-V2S2FVAM-CaPZ1SUE.js → gitGraphDiagram-V2S2FVAM-D7RJFr3i.js} +1 -1
  27. package/dist/assets/{graph-DjLsaDIL.js → graph-CJZvtDnb.js} +1 -1
  28. package/dist/assets/index-CARUdnbv.js +795 -0
  29. package/dist/assets/index-DcCYJX3c.css +1 -0
  30. package/dist/assets/{infoDiagram-HS3SLOUP-D_9DW9VU.js → infoDiagram-HS3SLOUP-CcSzbqKG.js} +1 -1
  31. package/dist/assets/{isUndefined-BkijP54r.js → isUndefined-BbMiFvTc.js} +1 -1
  32. package/dist/assets/{journeyDiagram-XKPGCS4Q-xxRWc5wn.js → journeyDiagram-XKPGCS4Q-DTmP5k2H.js} +1 -1
  33. package/dist/assets/{kanban-definition-3W4ZIXB7-BCI2NbpB.js → kanban-definition-3W4ZIXB7-BSA4gDhk.js} +1 -1
  34. package/dist/assets/{layout-M840MAqW.js → layout-D3BxIqM1.js} +1 -1
  35. package/dist/assets/{linear--_gMRK53.js → linear-CemsbS51.js} +1 -1
  36. package/dist/assets/{min-D06s3WKk.js → min-DkRMnH62.js} +1 -1
  37. package/dist/assets/{mindmap-definition-VGOIOE7T-B1-ALKFj.js → mindmap-definition-VGOIOE7T-CNj0QY6e.js} +1 -1
  38. package/dist/assets/{pieDiagram-ADFJNKIX-76kjoNcf.js → pieDiagram-ADFJNKIX-CPQsqbb4.js} +1 -1
  39. package/dist/assets/{quadrantDiagram-AYHSOK5B-O_d3GTQl.js → quadrantDiagram-AYHSOK5B-CPX25nd3.js} +1 -1
  40. package/dist/assets/{requirementDiagram-UZGBJVZJ-CasSIIPr.js → requirementDiagram-UZGBJVZJ-7mxxRNiB.js} +1 -1
  41. package/dist/assets/{sankeyDiagram-TZEHDZUN-CL_kG7jD.js → sankeyDiagram-TZEHDZUN-CPhitUZ9.js} +1 -1
  42. package/dist/assets/{sequenceDiagram-WL72ISMW-RYnztN0k.js → sequenceDiagram-WL72ISMW-BwLMsC7F.js} +1 -1
  43. package/dist/assets/{stateDiagram-FKZM4ZOC-NHFLaLiz.js → stateDiagram-FKZM4ZOC-Crx9CUqD.js} +1 -1
  44. package/dist/assets/stateDiagram-v2-4FDKWEC3-elh6u9Fl.js +1 -0
  45. package/dist/assets/{timeline-definition-IT6M3QCI-BH1cZoua.js → timeline-definition-IT6M3QCI-DDh1Iim0.js} +1 -1
  46. package/dist/assets/{treemap-GDKQZRPO-QAQDPzuO.js → treemap-GDKQZRPO-Bc6lNY0z.js} +1 -1
  47. package/dist/assets/{xychartDiagram-PRI3JC2R-C3M4ZzCP.js → xychartDiagram-PRI3JC2R-BAGZwI3e.js} +1 -1
  48. package/dist/index.html +2 -2
  49. package/package.json +1 -1
  50. package/src/features/account/pages/service-tokens.tsx +13 -60
  51. package/src/features/workflow/components/panels/rjsf-cron-widget.tsx +421 -0
  52. package/src/features/workflow/components/panels/rjsf-theme.tsx +2 -0
  53. package/src/features/workflow/lib/workflow-storage-api.ts +103 -2
  54. package/src/features/workflow/lib/workflow-storage-helpers.ts +19 -0
  55. package/src/features/workflow/pages/workflow/components/workflow-config-sheet.tsx +26 -5
  56. package/src/features/workflow/pages/workflow/components/workflow-tab-content.tsx +64 -1
  57. package/src/features/workflow/pages/workflow/hooks/controller/build-layout-props.ts +5 -0
  58. package/src/lib/api.ts +2 -17
  59. package/dist/assets/channel-C3cH7p5A.js +0 -1
  60. package/dist/assets/classDiagram-2ON5EDUG-o6x5AEsV.js +0 -1
  61. package/dist/assets/classDiagram-v2-WZHVMYZB-o6x5AEsV.js +0 -1
  62. package/dist/assets/clone-CnJR7M_G.js +0 -1
  63. package/dist/assets/index-DSthAh3a.css +0 -1
  64. package/dist/assets/index-DlptXU2u.js +0 -795
  65. package/dist/assets/stateDiagram-v2-4FDKWEC3-3y5AUrwb.js +0 -1
@@ -1,4 +1,4 @@
1
- import{s as gi,g as xi,q as Xt,p as di,a as fi,b as pi,_ as a,l as Nt,H as mi,e as yi,y as bi,F as St,i as Ai,D as Yt,E as wi,K as Ci,aI as Si,R as Wt}from"./index-DlptXU2u.js";import{i as _i}from"./init-Gi6I4Gst.js";import{o as ki}from"./ordinal-Cboi1Yqb.js";import{l as zt}from"./linear--_gMRK53.js";import"./defaultLocale-C4B-KCzX.js";function Ri(e,t,i){e=+e,t=+t,i=(n=arguments.length)<2?(t=e,e=0,1):n<3?1:+i;for(var s=-1,n=Math.max(0,Math.ceil((t-e)/i))|0,o=new Array(n);++s<n;)o[s]=e+s*i;return o}function yt(){var e=ki().unknown(void 0),t=e.domain,i=e.range,s=0,n=1,o,g,m=!1,p=0,k=0,v=.5;delete e.unknown;function C(){var b=t().length,E=n<s,D=E?n:s,P=E?s:n;o=(P-D)/Math.max(1,b-p+k*2),m&&(o=Math.floor(o)),D+=(P-D-o*(b-p))*v,g=o*(1-p),m&&(D=Math.round(D),g=Math.round(g));var I=Ri(b).map(function(y){return D+o*y});return i(E?I.reverse():I)}return e.domain=function(b){return arguments.length?(t(b),C()):t()},e.range=function(b){return arguments.length?([s,n]=b,s=+s,n=+n,C()):[s,n]},e.rangeRound=function(b){return[s,n]=b,s=+s,n=+n,m=!0,C()},e.bandwidth=function(){return g},e.step=function(){return o},e.round=function(b){return arguments.length?(m=!!b,C()):m},e.padding=function(b){return arguments.length?(p=Math.min(1,k=+b),C()):p},e.paddingInner=function(b){return arguments.length?(p=Math.min(1,b),C()):p},e.paddingOuter=function(b){return arguments.length?(k=+b,C()):k},e.align=function(b){return arguments.length?(v=Math.max(0,Math.min(1,b)),C()):v},e.copy=function(){return yt(t(),[s,n]).round(m).paddingInner(p).paddingOuter(k).align(v)},_i.apply(C(),arguments)}var bt=(function(){var e=a(function(F,h,u,x){for(u=u||{},x=F.length;x--;u[F[x]]=h);return u},"o"),t=[1,10,12,14,16,18,19,21,23],i=[2,6],s=[1,3],n=[1,5],o=[1,6],g=[1,7],m=[1,5,10,12,14,16,18,19,21,23,34,35,36],p=[1,25],k=[1,26],v=[1,28],C=[1,29],b=[1,30],E=[1,31],D=[1,32],P=[1,33],I=[1,34],y=[1,35],_=[1,36],c=[1,37],W=[1,43],z=[1,42],U=[1,47],X=[1,50],l=[1,10,12,14,16,18,19,21,23,34,35,36],L=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36],S=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36,41,42,43,44,45,46,47,48,49,50],R=[1,64],$={trace:a(function(){},"trace"),yy:{},symbols_:{error:2,start:3,eol:4,XYCHART:5,chartConfig:6,document:7,CHART_ORIENTATION:8,statement:9,title:10,text:11,X_AXIS:12,parseXAxis:13,Y_AXIS:14,parseYAxis:15,LINE:16,plotData:17,BAR:18,acc_title:19,acc_title_value:20,acc_descr:21,acc_descr_value:22,acc_descr_multiline_value:23,SQUARE_BRACES_START:24,commaSeparatedNumbers:25,SQUARE_BRACES_END:26,NUMBER_WITH_DECIMAL:27,COMMA:28,xAxisData:29,bandData:30,ARROW_DELIMITER:31,commaSeparatedTexts:32,yAxisData:33,NEWLINE:34,SEMI:35,EOF:36,alphaNum:37,STR:38,MD_STR:39,alphaNumToken:40,AMP:41,NUM:42,ALPHA:43,PLUS:44,EQUALS:45,MULT:46,DOT:47,BRKT:48,MINUS:49,UNDERSCORE:50,$accept:0,$end:1},terminals_:{2:"error",5:"XYCHART",8:"CHART_ORIENTATION",10:"title",12:"X_AXIS",14:"Y_AXIS",16:"LINE",18:"BAR",19:"acc_title",20:"acc_title_value",21:"acc_descr",22:"acc_descr_value",23:"acc_descr_multiline_value",24:"SQUARE_BRACES_START",26:"SQUARE_BRACES_END",27:"NUMBER_WITH_DECIMAL",28:"COMMA",31:"ARROW_DELIMITER",34:"NEWLINE",35:"SEMI",36:"EOF",38:"STR",39:"MD_STR",41:"AMP",42:"NUM",43:"ALPHA",44:"PLUS",45:"EQUALS",46:"MULT",47:"DOT",48:"BRKT",49:"MINUS",50:"UNDERSCORE"},productions_:[0,[3,2],[3,3],[3,2],[3,1],[6,1],[7,0],[7,2],[9,2],[9,2],[9,2],[9,2],[9,2],[9,3],[9,2],[9,3],[9,2],[9,2],[9,1],[17,3],[25,3],[25,1],[13,1],[13,2],[13,1],[29,1],[29,3],[30,3],[32,3],[32,1],[15,1],[15,2],[15,1],[33,3],[4,1],[4,1],[4,1],[11,1],[11,1],[11,1],[37,1],[37,2],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1]],performAction:a(function(h,u,x,d,w,r,at){var f=r.length-1;switch(w){case 5:d.setOrientation(r[f]);break;case 9:d.setDiagramTitle(r[f].text.trim());break;case 12:d.setLineData({text:"",type:"text"},r[f]);break;case 13:d.setLineData(r[f-1],r[f]);break;case 14:d.setBarData({text:"",type:"text"},r[f]);break;case 15:d.setBarData(r[f-1],r[f]);break;case 16:this.$=r[f].trim(),d.setAccTitle(this.$);break;case 17:case 18:this.$=r[f].trim(),d.setAccDescription(this.$);break;case 19:this.$=r[f-1];break;case 20:this.$=[Number(r[f-2]),...r[f]];break;case 21:this.$=[Number(r[f])];break;case 22:d.setXAxisTitle(r[f]);break;case 23:d.setXAxisTitle(r[f-1]);break;case 24:d.setXAxisTitle({type:"text",text:""});break;case 25:d.setXAxisBand(r[f]);break;case 26:d.setXAxisRangeData(Number(r[f-2]),Number(r[f]));break;case 27:this.$=r[f-1];break;case 28:this.$=[r[f-2],...r[f]];break;case 29:this.$=[r[f]];break;case 30:d.setYAxisTitle(r[f]);break;case 31:d.setYAxisTitle(r[f-1]);break;case 32:d.setYAxisTitle({type:"text",text:""});break;case 33:d.setYAxisRangeData(Number(r[f-2]),Number(r[f]));break;case 37:this.$={text:r[f],type:"text"};break;case 38:this.$={text:r[f],type:"text"};break;case 39:this.$={text:r[f],type:"markdown"};break;case 40:this.$=r[f];break;case 41:this.$=r[f-1]+""+r[f];break}},"anonymous"),table:[e(t,i,{3:1,4:2,7:4,5:s,34:n,35:o,36:g}),{1:[3]},e(t,i,{4:2,7:4,3:8,5:s,34:n,35:o,36:g}),e(t,i,{4:2,7:4,6:9,3:10,5:s,8:[1,11],34:n,35:o,36:g}),{1:[2,4],9:12,10:[1,13],12:[1,14],14:[1,15],16:[1,16],18:[1,17],19:[1,18],21:[1,19],23:[1,20]},e(m,[2,34]),e(m,[2,35]),e(m,[2,36]),{1:[2,1]},e(t,i,{4:2,7:4,3:21,5:s,34:n,35:o,36:g}),{1:[2,3]},e(m,[2,5]),e(t,[2,7],{4:22,34:n,35:o,36:g}),{11:23,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:39,13:38,24:W,27:z,29:40,30:41,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:45,15:44,27:U,33:46,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:49,17:48,24:X,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:52,17:51,24:X,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{20:[1,53]},{22:[1,54]},e(l,[2,18]),{1:[2,2]},e(l,[2,8]),e(l,[2,9]),e(L,[2,37],{40:55,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c}),e(L,[2,38]),e(L,[2,39]),e(S,[2,40]),e(S,[2,42]),e(S,[2,43]),e(S,[2,44]),e(S,[2,45]),e(S,[2,46]),e(S,[2,47]),e(S,[2,48]),e(S,[2,49]),e(S,[2,50]),e(S,[2,51]),e(l,[2,10]),e(l,[2,22],{30:41,29:56,24:W,27:z}),e(l,[2,24]),e(l,[2,25]),{31:[1,57]},{11:59,32:58,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},e(l,[2,11]),e(l,[2,30],{33:60,27:U}),e(l,[2,32]),{31:[1,61]},e(l,[2,12]),{17:62,24:X},{25:63,27:R},e(l,[2,14]),{17:65,24:X},e(l,[2,16]),e(l,[2,17]),e(S,[2,41]),e(l,[2,23]),{27:[1,66]},{26:[1,67]},{26:[2,29],28:[1,68]},e(l,[2,31]),{27:[1,69]},e(l,[2,13]),{26:[1,70]},{26:[2,21],28:[1,71]},e(l,[2,15]),e(l,[2,26]),e(l,[2,27]),{11:59,32:72,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},e(l,[2,33]),e(l,[2,19]),{25:73,27:R},{26:[2,28]},{26:[2,20]}],defaultActions:{8:[2,1],10:[2,3],21:[2,2],72:[2,28],73:[2,20]},parseError:a(function(h,u){if(u.recoverable)this.trace(h);else{var x=new Error(h);throw x.hash=u,x}},"parseError"),parse:a(function(h){var u=this,x=[0],d=[],w=[null],r=[],at=this.table,f="",lt=0,It=0,hi=2,Mt=1,li=r.slice.call(arguments,1),T=Object.create(this.lexer),Y={yy:{}};for(var dt in this.yy)Object.prototype.hasOwnProperty.call(this.yy,dt)&&(Y.yy[dt]=this.yy[dt]);T.setInput(h,Y.yy),Y.yy.lexer=T,Y.yy.parser=this,typeof T.yylloc>"u"&&(T.yylloc={});var ft=T.yylloc;r.push(ft);var ci=T.options&&T.options.ranges;typeof Y.yy.parseError=="function"?this.parseError=Y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function ui(V){x.length=x.length-2*V,w.length=w.length-V,r.length=r.length-V}a(ui,"popStack");function Vt(){var V;return V=d.pop()||T.lex()||Mt,typeof V!="number"&&(V instanceof Array&&(d=V,V=d.pop()),V=u.symbols_[V]||V),V}a(Vt,"lex");for(var M,H,B,pt,q={},ct,O,Bt,ut;;){if(H=x[x.length-1],this.defaultActions[H]?B=this.defaultActions[H]:((M===null||typeof M>"u")&&(M=Vt()),B=at[H]&&at[H][M]),typeof B>"u"||!B.length||!B[0]){var mt="";ut=[];for(ct in at[H])this.terminals_[ct]&&ct>hi&&ut.push("'"+this.terminals_[ct]+"'");T.showPosition?mt="Parse error on line "+(lt+1)+`:
1
+ import{s as gi,g as xi,q as Xt,p as di,a as fi,b as pi,_ as a,l as Nt,H as mi,e as yi,y as bi,F as St,i as Ai,D as Yt,E as wi,K as Ci,aI as Si,R as Wt}from"./index-CARUdnbv.js";import{i as _i}from"./init-Gi6I4Gst.js";import{o as ki}from"./ordinal-Cboi1Yqb.js";import{l as zt}from"./linear-CemsbS51.js";import"./defaultLocale-C4B-KCzX.js";function Ri(e,t,i){e=+e,t=+t,i=(n=arguments.length)<2?(t=e,e=0,1):n<3?1:+i;for(var s=-1,n=Math.max(0,Math.ceil((t-e)/i))|0,o=new Array(n);++s<n;)o[s]=e+s*i;return o}function yt(){var e=ki().unknown(void 0),t=e.domain,i=e.range,s=0,n=1,o,g,m=!1,p=0,k=0,v=.5;delete e.unknown;function C(){var b=t().length,E=n<s,D=E?n:s,P=E?s:n;o=(P-D)/Math.max(1,b-p+k*2),m&&(o=Math.floor(o)),D+=(P-D-o*(b-p))*v,g=o*(1-p),m&&(D=Math.round(D),g=Math.round(g));var I=Ri(b).map(function(y){return D+o*y});return i(E?I.reverse():I)}return e.domain=function(b){return arguments.length?(t(b),C()):t()},e.range=function(b){return arguments.length?([s,n]=b,s=+s,n=+n,C()):[s,n]},e.rangeRound=function(b){return[s,n]=b,s=+s,n=+n,m=!0,C()},e.bandwidth=function(){return g},e.step=function(){return o},e.round=function(b){return arguments.length?(m=!!b,C()):m},e.padding=function(b){return arguments.length?(p=Math.min(1,k=+b),C()):p},e.paddingInner=function(b){return arguments.length?(p=Math.min(1,b),C()):p},e.paddingOuter=function(b){return arguments.length?(k=+b,C()):k},e.align=function(b){return arguments.length?(v=Math.max(0,Math.min(1,b)),C()):v},e.copy=function(){return yt(t(),[s,n]).round(m).paddingInner(p).paddingOuter(k).align(v)},_i.apply(C(),arguments)}var bt=(function(){var e=a(function(F,h,u,x){for(u=u||{},x=F.length;x--;u[F[x]]=h);return u},"o"),t=[1,10,12,14,16,18,19,21,23],i=[2,6],s=[1,3],n=[1,5],o=[1,6],g=[1,7],m=[1,5,10,12,14,16,18,19,21,23,34,35,36],p=[1,25],k=[1,26],v=[1,28],C=[1,29],b=[1,30],E=[1,31],D=[1,32],P=[1,33],I=[1,34],y=[1,35],_=[1,36],c=[1,37],W=[1,43],z=[1,42],U=[1,47],X=[1,50],l=[1,10,12,14,16,18,19,21,23,34,35,36],L=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36],S=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36,41,42,43,44,45,46,47,48,49,50],R=[1,64],$={trace:a(function(){},"trace"),yy:{},symbols_:{error:2,start:3,eol:4,XYCHART:5,chartConfig:6,document:7,CHART_ORIENTATION:8,statement:9,title:10,text:11,X_AXIS:12,parseXAxis:13,Y_AXIS:14,parseYAxis:15,LINE:16,plotData:17,BAR:18,acc_title:19,acc_title_value:20,acc_descr:21,acc_descr_value:22,acc_descr_multiline_value:23,SQUARE_BRACES_START:24,commaSeparatedNumbers:25,SQUARE_BRACES_END:26,NUMBER_WITH_DECIMAL:27,COMMA:28,xAxisData:29,bandData:30,ARROW_DELIMITER:31,commaSeparatedTexts:32,yAxisData:33,NEWLINE:34,SEMI:35,EOF:36,alphaNum:37,STR:38,MD_STR:39,alphaNumToken:40,AMP:41,NUM:42,ALPHA:43,PLUS:44,EQUALS:45,MULT:46,DOT:47,BRKT:48,MINUS:49,UNDERSCORE:50,$accept:0,$end:1},terminals_:{2:"error",5:"XYCHART",8:"CHART_ORIENTATION",10:"title",12:"X_AXIS",14:"Y_AXIS",16:"LINE",18:"BAR",19:"acc_title",20:"acc_title_value",21:"acc_descr",22:"acc_descr_value",23:"acc_descr_multiline_value",24:"SQUARE_BRACES_START",26:"SQUARE_BRACES_END",27:"NUMBER_WITH_DECIMAL",28:"COMMA",31:"ARROW_DELIMITER",34:"NEWLINE",35:"SEMI",36:"EOF",38:"STR",39:"MD_STR",41:"AMP",42:"NUM",43:"ALPHA",44:"PLUS",45:"EQUALS",46:"MULT",47:"DOT",48:"BRKT",49:"MINUS",50:"UNDERSCORE"},productions_:[0,[3,2],[3,3],[3,2],[3,1],[6,1],[7,0],[7,2],[9,2],[9,2],[9,2],[9,2],[9,2],[9,3],[9,2],[9,3],[9,2],[9,2],[9,1],[17,3],[25,3],[25,1],[13,1],[13,2],[13,1],[29,1],[29,3],[30,3],[32,3],[32,1],[15,1],[15,2],[15,1],[33,3],[4,1],[4,1],[4,1],[11,1],[11,1],[11,1],[37,1],[37,2],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1]],performAction:a(function(h,u,x,d,w,r,at){var f=r.length-1;switch(w){case 5:d.setOrientation(r[f]);break;case 9:d.setDiagramTitle(r[f].text.trim());break;case 12:d.setLineData({text:"",type:"text"},r[f]);break;case 13:d.setLineData(r[f-1],r[f]);break;case 14:d.setBarData({text:"",type:"text"},r[f]);break;case 15:d.setBarData(r[f-1],r[f]);break;case 16:this.$=r[f].trim(),d.setAccTitle(this.$);break;case 17:case 18:this.$=r[f].trim(),d.setAccDescription(this.$);break;case 19:this.$=r[f-1];break;case 20:this.$=[Number(r[f-2]),...r[f]];break;case 21:this.$=[Number(r[f])];break;case 22:d.setXAxisTitle(r[f]);break;case 23:d.setXAxisTitle(r[f-1]);break;case 24:d.setXAxisTitle({type:"text",text:""});break;case 25:d.setXAxisBand(r[f]);break;case 26:d.setXAxisRangeData(Number(r[f-2]),Number(r[f]));break;case 27:this.$=r[f-1];break;case 28:this.$=[r[f-2],...r[f]];break;case 29:this.$=[r[f]];break;case 30:d.setYAxisTitle(r[f]);break;case 31:d.setYAxisTitle(r[f-1]);break;case 32:d.setYAxisTitle({type:"text",text:""});break;case 33:d.setYAxisRangeData(Number(r[f-2]),Number(r[f]));break;case 37:this.$={text:r[f],type:"text"};break;case 38:this.$={text:r[f],type:"text"};break;case 39:this.$={text:r[f],type:"markdown"};break;case 40:this.$=r[f];break;case 41:this.$=r[f-1]+""+r[f];break}},"anonymous"),table:[e(t,i,{3:1,4:2,7:4,5:s,34:n,35:o,36:g}),{1:[3]},e(t,i,{4:2,7:4,3:8,5:s,34:n,35:o,36:g}),e(t,i,{4:2,7:4,6:9,3:10,5:s,8:[1,11],34:n,35:o,36:g}),{1:[2,4],9:12,10:[1,13],12:[1,14],14:[1,15],16:[1,16],18:[1,17],19:[1,18],21:[1,19],23:[1,20]},e(m,[2,34]),e(m,[2,35]),e(m,[2,36]),{1:[2,1]},e(t,i,{4:2,7:4,3:21,5:s,34:n,35:o,36:g}),{1:[2,3]},e(m,[2,5]),e(t,[2,7],{4:22,34:n,35:o,36:g}),{11:23,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:39,13:38,24:W,27:z,29:40,30:41,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:45,15:44,27:U,33:46,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:49,17:48,24:X,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:52,17:51,24:X,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{20:[1,53]},{22:[1,54]},e(l,[2,18]),{1:[2,2]},e(l,[2,8]),e(l,[2,9]),e(L,[2,37],{40:55,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c}),e(L,[2,38]),e(L,[2,39]),e(S,[2,40]),e(S,[2,42]),e(S,[2,43]),e(S,[2,44]),e(S,[2,45]),e(S,[2,46]),e(S,[2,47]),e(S,[2,48]),e(S,[2,49]),e(S,[2,50]),e(S,[2,51]),e(l,[2,10]),e(l,[2,22],{30:41,29:56,24:W,27:z}),e(l,[2,24]),e(l,[2,25]),{31:[1,57]},{11:59,32:58,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},e(l,[2,11]),e(l,[2,30],{33:60,27:U}),e(l,[2,32]),{31:[1,61]},e(l,[2,12]),{17:62,24:X},{25:63,27:R},e(l,[2,14]),{17:65,24:X},e(l,[2,16]),e(l,[2,17]),e(S,[2,41]),e(l,[2,23]),{27:[1,66]},{26:[1,67]},{26:[2,29],28:[1,68]},e(l,[2,31]),{27:[1,69]},e(l,[2,13]),{26:[1,70]},{26:[2,21],28:[1,71]},e(l,[2,15]),e(l,[2,26]),e(l,[2,27]),{11:59,32:72,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},e(l,[2,33]),e(l,[2,19]),{25:73,27:R},{26:[2,28]},{26:[2,20]}],defaultActions:{8:[2,1],10:[2,3],21:[2,2],72:[2,28],73:[2,20]},parseError:a(function(h,u){if(u.recoverable)this.trace(h);else{var x=new Error(h);throw x.hash=u,x}},"parseError"),parse:a(function(h){var u=this,x=[0],d=[],w=[null],r=[],at=this.table,f="",lt=0,It=0,hi=2,Mt=1,li=r.slice.call(arguments,1),T=Object.create(this.lexer),Y={yy:{}};for(var dt in this.yy)Object.prototype.hasOwnProperty.call(this.yy,dt)&&(Y.yy[dt]=this.yy[dt]);T.setInput(h,Y.yy),Y.yy.lexer=T,Y.yy.parser=this,typeof T.yylloc>"u"&&(T.yylloc={});var ft=T.yylloc;r.push(ft);var ci=T.options&&T.options.ranges;typeof Y.yy.parseError=="function"?this.parseError=Y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function ui(V){x.length=x.length-2*V,w.length=w.length-V,r.length=r.length-V}a(ui,"popStack");function Vt(){var V;return V=d.pop()||T.lex()||Mt,typeof V!="number"&&(V instanceof Array&&(d=V,V=d.pop()),V=u.symbols_[V]||V),V}a(Vt,"lex");for(var M,H,B,pt,q={},ct,O,Bt,ut;;){if(H=x[x.length-1],this.defaultActions[H]?B=this.defaultActions[H]:((M===null||typeof M>"u")&&(M=Vt()),B=at[H]&&at[H][M]),typeof B>"u"||!B.length||!B[0]){var mt="";ut=[];for(ct in at[H])this.terminals_[ct]&&ct>hi&&ut.push("'"+this.terminals_[ct]+"'");T.showPosition?mt="Parse error on line "+(lt+1)+`:
2
2
  `+T.showPosition()+`
3
3
  Expecting `+ut.join(", ")+", got '"+(this.terminals_[M]||M)+"'":mt="Parse error on line "+(lt+1)+": Unexpected "+(M==Mt?"end of input":"'"+(this.terminals_[M]||M)+"'"),this.parseError(mt,{text:T.match,token:this.terminals_[M]||M,line:T.yylineno,loc:ft,expected:ut})}if(B[0]instanceof Array&&B.length>1)throw new Error("Parse Error: multiple actions possible at state: "+H+", token: "+M);switch(B[0]){case 1:x.push(M),w.push(T.yytext),r.push(T.yylloc),x.push(B[1]),M=null,It=T.yyleng,f=T.yytext,lt=T.yylineno,ft=T.yylloc;break;case 2:if(O=this.productions_[B[1]][1],q.$=w[w.length-O],q._$={first_line:r[r.length-(O||1)].first_line,last_line:r[r.length-1].last_line,first_column:r[r.length-(O||1)].first_column,last_column:r[r.length-1].last_column},ci&&(q._$.range=[r[r.length-(O||1)].range[0],r[r.length-1].range[1]]),pt=this.performAction.apply(q,[f,It,lt,Y.yy,B[1],w,r].concat(li)),typeof pt<"u")return pt;O&&(x=x.slice(0,-1*O*2),w=w.slice(0,-1*O),r=r.slice(0,-1*O)),x.push(this.productions_[B[1]][0]),w.push(q.$),r.push(q._$),Bt=at[x[x.length-2]][x[x.length-1]],x.push(Bt);break;case 3:return!0}}return!0},"parse")},Et=(function(){var F={EOF:1,parseError:a(function(u,x){if(this.yy.parser)this.yy.parser.parseError(u,x);else throw new Error(u)},"parseError"),setInput:a(function(h,u){return this.yy=u||this.yy||{},this._input=h,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:a(function(){var h=this._input[0];this.yytext+=h,this.yyleng++,this.offset++,this.match+=h,this.matched+=h;var u=h.match(/(?:\r\n?|\n).*/g);return u?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),h},"input"),unput:a(function(h){var u=h.length,x=h.split(/(?:\r\n?|\n)/g);this._input=h+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-u),this.offset-=u;var d=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),x.length-1&&(this.yylineno-=x.length-1);var w=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:x?(x.length===d.length?this.yylloc.first_column:0)+d[d.length-x.length].length-x[0].length:this.yylloc.first_column-u},this.options.ranges&&(this.yylloc.range=[w[0],w[0]+this.yyleng-u]),this.yyleng=this.yytext.length,this},"unput"),more:a(function(){return this._more=!0,this},"more"),reject:a(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).
4
4
  `+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:a(function(h){this.unput(this.match.slice(h))},"less"),pastInput:a(function(){var h=this.matched.substr(0,this.matched.length-this.match.length);return(h.length>20?"...":"")+h.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:a(function(){var h=this.match;return h.length<20&&(h+=this._input.substr(0,20-h.length)),(h.substr(0,20)+(h.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:a(function(){var h=this.pastInput(),u=new Array(h.length+1).join("-");return h+this.upcomingInput()+`
package/dist/index.html CHANGED
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.ico" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Orcheo</title>
8
- <script type="module" crossorigin src="/assets/index-DlptXU2u.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-DSthAh3a.css">
8
+ <script type="module" crossorigin src="/assets/index-CARUdnbv.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-DcCYJX3c.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "orcheo-studio",
3
3
  "private": false,
4
- "version": "0.24.0",
4
+ "version": "0.24.1",
5
5
  "type": "module",
6
6
  "description": "Orcheo UI for Orcheo workflow orchestration platform",
7
7
  "keywords": [
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useState } from "react";
2
- import { Copy, KeyRound, RotateCw, Trash2 } from "lucide-react";
2
+ import { Copy, KeyRound, Trash2 } from "lucide-react";
3
3
  import { Button } from "@/design-system/ui/button";
4
4
  import { Input } from "@/design-system/ui/input";
5
5
  import { Label } from "@/design-system/ui/label";
@@ -43,7 +43,6 @@ import {
43
43
  getActiveWorkspace,
44
44
  listServiceTokens,
45
45
  revokeServiceToken,
46
- rotateServiceToken,
47
46
  type ServiceToken,
48
47
  } from "@/lib/api";
49
48
 
@@ -63,8 +62,6 @@ const EXPIRY_OPTIONS = [
63
62
  { value: "30d", label: "30 days", seconds: 2592000 },
64
63
  ] as const;
65
64
 
66
- const ROTATION_OVERLAP_SECONDS = 300;
67
-
68
65
  const formatDate = (value: string | null | undefined): string =>
69
66
  value ? new Date(value).toLocaleString() : "—";
70
67
 
@@ -90,13 +87,12 @@ export default function ServiceTokens() {
90
87
  const [workspaceName, setWorkspaceName] = useState<string | null>(null);
91
88
 
92
89
  const [createOpen, setCreateOpen] = useState(false);
93
- const [identifier, setIdentifier] = useState("");
90
+ const [name, setName] = useState("");
94
91
  const [selectedScopes, setSelectedScopes] = useState<Set<string>>(new Set());
95
92
  const [expiry, setExpiry] = useState<string>("never");
96
93
  const [isMinting, setIsMinting] = useState(false);
97
94
 
98
95
  const [revealed, setRevealed] = useState<RevealedSecret | null>(null);
99
- const [rotatingId, setRotatingId] = useState<string | null>(null);
100
96
  const [revokingId, setRevokingId] = useState<string | null>(null);
101
97
  const [revokeTarget, setRevokeTarget] = useState<string | null>(null);
102
98
 
@@ -137,7 +133,7 @@ export default function ServiceTokens() {
137
133
  }, []);
138
134
 
139
135
  const resetCreateForm = () => {
140
- setIdentifier("");
136
+ setName("");
141
137
  setSelectedScopes(new Set());
142
138
  setExpiry("never");
143
139
  };
@@ -161,14 +157,14 @@ export default function ServiceTokens() {
161
157
  (option) => option.value === expiry,
162
158
  );
163
159
  const token = await createServiceToken({
164
- identifier: identifier.trim() || undefined,
160
+ name: name.trim() || undefined,
165
161
  scopes: [...selectedScopes],
166
162
  expires_in_seconds: expiryOption?.seconds ?? undefined,
167
163
  });
168
164
  setTokens((prev) => [token, ...prev]);
169
165
  setRevealed({
170
166
  title: "API key created",
171
- identifier: token.identifier,
167
+ identifier: token.name ?? token.identifier,
172
168
  secret: token.secret ?? "",
173
169
  });
174
170
  resetCreateForm();
@@ -185,32 +181,6 @@ export default function ServiceTokens() {
185
181
  }
186
182
  };
187
183
 
188
- const handleRotate = async (tokenId: string) => {
189
- setRotatingId(tokenId);
190
- try {
191
- const rotated = await rotateServiceToken(
192
- tokenId,
193
- ROTATION_OVERLAP_SECONDS,
194
- );
195
- const tokenList = await listServiceTokens();
196
- setTokens(tokenList.tokens);
197
- setRevealed({
198
- title: "API key rotated",
199
- identifier: rotated.identifier,
200
- secret: rotated.secret ?? "",
201
- });
202
- toast({ title: "API key rotated" });
203
- } catch (err) {
204
- toast({
205
- title: "Failed to rotate API key",
206
- description: err instanceof Error ? err.message : undefined,
207
- variant: "destructive",
208
- });
209
- } finally {
210
- setRotatingId(null);
211
- }
212
- };
213
-
214
184
  const handleRevoke = async () => {
215
185
  const tokenId = revokeTarget;
216
186
  if (!tokenId) {
@@ -277,7 +247,7 @@ export default function ServiceTokens() {
277
247
  <Table>
278
248
  <TableHeader>
279
249
  <TableRow>
280
- <TableHead>Identifier</TableHead>
250
+ <TableHead>Name</TableHead>
281
251
  <TableHead>Key</TableHead>
282
252
  <TableHead>Scopes</TableHead>
283
253
  <TableHead>Issued</TableHead>
@@ -294,7 +264,7 @@ export default function ServiceTokens() {
294
264
  return (
295
265
  <TableRow key={token.identifier}>
296
266
  <TableCell className="font-mono text-sm">
297
- {token.identifier}
267
+ {token.name ?? token.identifier}
298
268
  </TableCell>
299
269
  <TableCell className="font-mono text-sm text-muted-foreground">
300
270
  {token.secret_preview
@@ -332,30 +302,13 @@ export default function ServiceTokens() {
332
302
  </TableCell>
333
303
  <TableCell className="text-right">
334
304
  <div className="flex justify-end gap-1">
335
- <Button
336
- variant="ghost"
337
- size="sm"
338
- onClick={() => void handleRotate(token.identifier)}
339
- disabled={
340
- !isActive ||
341
- rotatingId === token.identifier ||
342
- revokingId === token.identifier
343
- }
344
- >
345
- <RotateCw className="mr-2 h-4 w-4" />
346
- {rotatingId === token.identifier
347
- ? "Rotating..."
348
- : "Rotate"}
349
- </Button>
350
305
  <Button
351
306
  variant="ghost"
352
307
  size="sm"
353
308
  className="text-destructive hover:text-destructive"
354
309
  onClick={() => setRevokeTarget(token.identifier)}
355
310
  disabled={
356
- !isActive ||
357
- rotatingId === token.identifier ||
358
- revokingId === token.identifier
311
+ !isActive || revokingId === token.identifier
359
312
  }
360
313
  >
361
314
  <Trash2 className="mr-2 h-4 w-4" />
@@ -391,12 +344,12 @@ export default function ServiceTokens() {
391
344
  </DialogHeader>
392
345
  <div className="space-y-4">
393
346
  <div className="space-y-1.5">
394
- <Label htmlFor="token-identifier">Identifier (optional)</Label>
347
+ <Label htmlFor="token-name">Name (optional)</Label>
395
348
  <Input
396
- id="token-identifier"
397
- placeholder="Auto-generated if left blank"
398
- value={identifier}
399
- onChange={(event) => setIdentifier(event.target.value)}
349
+ id="token-name"
350
+ placeholder="e.g. Local, CI, Production"
351
+ value={name}
352
+ onChange={(event) => setName(event.target.value)}
400
353
  disabled={isMinting}
401
354
  />
402
355
  </div>
@@ -0,0 +1,421 @@
1
+ /* eslint-disable react-refresh/only-export-components */
2
+ /**
3
+ * Visual cron-expression builder widget.
4
+ *
5
+ * Renders the classic "Every <freq> on <days> at <hh>:<mm>" row instead of a
6
+ * raw string input, but still stores a standard 5-field cron expression
7
+ * (`minute hour day-of-month month day-of-week`) as the field value so the
8
+ * backend scheduler keeps receiving a plain string.
9
+ *
10
+ * It understands day-of-week ranges (e.g. `1-5`) on input. Expressions that the
11
+ * builder cannot represent losslessly (lists/ranges in minute or hour, specific
12
+ * months, combined day-of-month + day-of-week, named tokens, ...) fall back to a
13
+ * raw text input so editing never silently rewrites them.
14
+ */
15
+
16
+ import React from "react";
17
+ import { ChevronDown } from "lucide-react";
18
+ import { WidgetProps } from "@rjsf/utils";
19
+ import { cn } from "@/lib/utils";
20
+ import {
21
+ Select,
22
+ SelectContent,
23
+ SelectItem,
24
+ SelectTrigger,
25
+ SelectValue,
26
+ } from "@/design-system/ui/select";
27
+ import {
28
+ DropdownMenu,
29
+ DropdownMenuCheckboxItem,
30
+ DropdownMenuContent,
31
+ DropdownMenuTrigger,
32
+ } from "@/design-system/ui/dropdown-menu";
33
+ import { Input } from "@/design-system/ui/input";
34
+
35
+ type Frequency = "minute" | "hour" | "day" | "week" | "month";
36
+
37
+ interface CronParts {
38
+ /** Step interval (in minutes) used by the "minute" frequency, e.g. star-slash-30. */
39
+ interval: number;
40
+ /** Step interval (in hours) used by the "hour" frequency, e.g. star-slash-2. */
41
+ hourInterval: number;
42
+ minute: number;
43
+ hour: number;
44
+ dayOfMonth: number;
45
+ daysOfWeek: number[];
46
+ }
47
+
48
+ const DAY_LABELS = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
49
+
50
+ const FREQUENCY_OPTIONS: Array<{ value: Frequency; label: string }> = [
51
+ { value: "minute", label: "minutes" },
52
+ { value: "hour", label: "hours" },
53
+ { value: "day", label: "day" },
54
+ { value: "week", label: "week" },
55
+ { value: "month", label: "month" },
56
+ ];
57
+
58
+ // Common step intervals offered in the "every N minutes/hours" dropdowns. The
59
+ // current value is merged in (see `intervalOptions`) so hand-edited steps such
60
+ // as `*/7` still render a matching option.
61
+ const MINUTE_INTERVAL_PRESETS = [1, 2, 3, 5, 10, 15, 20, 30];
62
+ const HOUR_INTERVAL_PRESETS = [1, 2, 3, 4, 6, 8, 12];
63
+
64
+ const range = (count: number, start = 0) =>
65
+ Array.from({ length: count }, (_, i) => i + start);
66
+
67
+ const pad = (n: number) => String(n).padStart(2, "0");
68
+
69
+ const clampInt = (value: string, max: number, fallback: number) => {
70
+ const n = Number.parseInt(value, 10);
71
+ return Number.isFinite(n) && n >= 0 && n <= max ? n : fallback;
72
+ };
73
+
74
+ /** Parse a single cron field's first numeric token, falling back to `fallback`. */
75
+ const parseField = (field: string | undefined, max: number, fallback: number) =>
76
+ field && field !== "*" ? clampInt(field.split(/[,/-]/)[0], max, fallback) : fallback;
77
+
78
+ /** Extract the step N from a `*\/N` field (clamped to `max`), or undefined if it isn't a step. */
79
+ const parseStep = (field: string | undefined, max: number): number | undefined => {
80
+ const match = /^\*\/(\d+)$/.exec(field ?? "");
81
+ return match ? clampInt(match[1], max, 1) || 1 : undefined;
82
+ };
83
+
84
+ /** Parse the day-of-week field, supporting comma lists and ranges (e.g. "1,6", "1-5"). */
85
+ const parseDaysOfWeek = (field: string | undefined): number[] => {
86
+ if (!field || field === "*") return [];
87
+ const days = field
88
+ .split(",")
89
+ .flatMap((token) => {
90
+ const m = /^(\d)-(\d)$/.exec(token);
91
+ if (!m) return [clampInt(token, 7, -1)];
92
+ const from = clampInt(m[1], 7, -1);
93
+ const to = clampInt(m[2], 7, -1);
94
+ return from >= 0 && to >= from ? range(to - from + 1, from) : [-1];
95
+ })
96
+ .map((d) => (d === 7 ? 0 : d)) // normalise Sunday (cron allows 0 or 7)
97
+ .filter((d) => d >= 0 && d <= 6);
98
+ return Array.from(new Set(days));
99
+ };
100
+
101
+ const parseCron = (value: string | undefined): { freq: Frequency; parts: CronParts } => {
102
+ const [minute, hour, dom, , dow] = (value ?? "").trim().split(/\s+/);
103
+ const minuteStep = parseStep(minute, 59);
104
+ const hourStep = parseStep(hour, 23);
105
+ const parts: CronParts = {
106
+ interval: minuteStep ?? 1,
107
+ hourInterval: hourStep ?? 1,
108
+ minute: parseField(minute, 59, 0),
109
+ hour: parseField(hour, 23, 0),
110
+ dayOfMonth: dom && dom !== "*" ? clampInt(dom, 31, 1) || 1 : 1,
111
+ daysOfWeek: parseDaysOfWeek(dow),
112
+ };
113
+
114
+ let freq: Frequency = "day";
115
+ if (dow && dow !== "*") freq = "week";
116
+ else if (dom && dom !== "*") freq = "month";
117
+ // `*/30 * * * *` (step) or `* * * * *` (every minute) → minute interval.
118
+ else if (minuteStep !== undefined || minute === "*") freq = "minute";
119
+ // `M */2 * * *` (step) or `M * * * *` (every hour) → hour interval.
120
+ else if (hourStep !== undefined || hour === "*" || hour === undefined) freq = "hour";
121
+
122
+ return { freq, parts };
123
+ };
124
+
125
+ const buildCron = (freq: Frequency, parts: CronParts): string => {
126
+ const { interval, hourInterval, minute, hour, dayOfMonth, daysOfWeek } = parts;
127
+ switch (freq) {
128
+ case "minute":
129
+ // `*/1` is just "every minute"; emit the canonical wildcard form.
130
+ return interval === 1 ? "* * * * *" : `*/${interval} * * * *`;
131
+ case "hour":
132
+ // `*/1` is just "every hour"; emit the canonical wildcard form.
133
+ return hourInterval === 1
134
+ ? `${minute} * * * *`
135
+ : `${minute} */${hourInterval} * * *`;
136
+ case "day":
137
+ return `${minute} ${hour} * * *`;
138
+ case "week": {
139
+ const dow = daysOfWeek.length ? [...daysOfWeek].sort((a, b) => a - b).join(",") : "*";
140
+ return `${minute} ${hour} * * ${dow}`;
141
+ }
142
+ case "month":
143
+ return `${minute} ${hour} ${dayOfMonth} * *`;
144
+ default:
145
+ return `${minute} ${hour} * * *`;
146
+ }
147
+ };
148
+
149
+ /** Collapse `*\/1` to `*` so an explicit unit step compares equal to the wildcard. */
150
+ const collapseUnitStep = (field: string) => (field === "*/1" ? "*" : field);
151
+
152
+ /**
153
+ * Normalise a cron string to the canonical 5-field form the builder emits:
154
+ * drop redundant `*\/1` steps and expand day-of-week ranges to sorted comma
155
+ * lists. Returns `null` for anything malformed or unrepresentable (wrong field
156
+ * count, named tokens), so callers can fall back to raw editing.
157
+ */
158
+ const canonicalCron = (value: string | undefined): string | null => {
159
+ const raw = (value ?? "").trim();
160
+ if (!raw) return "";
161
+ const fields = raw.split(/\s+/);
162
+ if (fields.length !== 5) return null;
163
+ const [minute, hour, dom, month, dow] = fields.map(collapseUnitStep);
164
+ // Named tokens (MON, JAN) aren't supported by the visual builder.
165
+ if (/[a-z]/i.test(fields.join(""))) return null;
166
+ const days = parseDaysOfWeek(dow);
167
+ const dowField =
168
+ dow === "*" ? "*" : days.length ? [...days].sort((a, b) => a - b).join(",") : dow;
169
+ return [minute, hour, dom, month, dowField].join(" ");
170
+ };
171
+
172
+ /** True when `value` can be represented losslessly by the visual builder. */
173
+ const roundTrips = (value: string | undefined): boolean => {
174
+ const canonical = canonicalCron(value);
175
+ if (canonical === null) return false;
176
+ if (canonical === "") return true;
177
+ const { freq, parts } = parseCron(canonical);
178
+ return buildCron(freq, parts) === canonical;
179
+ };
180
+
181
+ /** Interval presets plus the current value, sorted and de-duplicated. */
182
+ const intervalOptions = (current: number, presets: number[]): number[] =>
183
+ Array.from(new Set([...presets, current])).sort((a, b) => a - b);
184
+
185
+ interface UnitSelectProps {
186
+ id?: string;
187
+ value: number;
188
+ max: number;
189
+ start?: number;
190
+ format?: (n: number) => string;
191
+ disabled?: boolean;
192
+ onChange: (value: number) => void;
193
+ }
194
+
195
+ function UnitSelect({
196
+ id,
197
+ value,
198
+ max,
199
+ start = 0,
200
+ format = String,
201
+ disabled,
202
+ onChange,
203
+ }: UnitSelectProps) {
204
+ return (
205
+ <Select
206
+ value={String(value)}
207
+ onValueChange={(v) => onChange(Number(v))}
208
+ disabled={disabled}
209
+ >
210
+ <SelectTrigger id={id} className="w-auto min-w-[4rem]">
211
+ <SelectValue />
212
+ </SelectTrigger>
213
+ <SelectContent className="max-h-60">
214
+ {range(max - start + 1, start).map((n) => (
215
+ <SelectItem key={n} value={String(n)}>
216
+ {format(n)}
217
+ </SelectItem>
218
+ ))}
219
+ </SelectContent>
220
+ </Select>
221
+ );
222
+ }
223
+
224
+ function CronWidget(props: WidgetProps) {
225
+ const { id, value, onChange, disabled, readonly } = props;
226
+ const isDisabled = disabled || readonly;
227
+
228
+ const representable = React.useMemo(() => roundTrips(value), [value]);
229
+ const { freq, parts } = React.useMemo(
230
+ () => parseCron(canonicalCron(value) ?? value),
231
+ [value],
232
+ );
233
+
234
+ const update = (nextFreq: Frequency, nextParts: CronParts) => {
235
+ onChange(buildCron(nextFreq, nextParts));
236
+ };
237
+
238
+ // Expressions the builder can't represent losslessly are edited as a raw cron
239
+ // string so tweaking one control never silently discards parts it can't model.
240
+ if (!representable) {
241
+ return (
242
+ <div className="flex flex-col gap-1">
243
+ <Input
244
+ id={id}
245
+ value={value ?? ""}
246
+ onChange={(e) => onChange(e.target.value)}
247
+ disabled={isDisabled}
248
+ spellCheck={false}
249
+ className="font-mono"
250
+ />
251
+ <span className="text-xs text-muted-foreground">
252
+ This schedule is too advanced for the visual builder — editing the raw
253
+ cron expression.
254
+ </span>
255
+ </div>
256
+ );
257
+ }
258
+
259
+ const setFreq = (nextFreq: Frequency) => {
260
+ // Default to a sensible day when switching to "week" with nothing selected.
261
+ const next =
262
+ nextFreq === "week" && parts.daysOfWeek.length === 0
263
+ ? { ...parts, daysOfWeek: [1] }
264
+ : parts;
265
+ update(nextFreq, next);
266
+ };
267
+
268
+ const toggleDay = (day: number, checked: boolean) => {
269
+ const daysOfWeek = checked
270
+ ? [...parts.daysOfWeek, day]
271
+ : parts.daysOfWeek.filter((d) => d !== day);
272
+ // Keep at least one day selected in "week" mode. An empty list serializes
273
+ // to a "day" cron, which would round-trip as a different frequency and
274
+ // bounce the user out to the raw-expression editor.
275
+ if (daysOfWeek.length === 0) {
276
+ return;
277
+ }
278
+ update("week", { ...parts, daysOfWeek });
279
+ };
280
+
281
+ const selectedDaysLabel =
282
+ parts.daysOfWeek.length === 0
283
+ ? "Select days"
284
+ : [...parts.daysOfWeek]
285
+ .sort((a, b) => a - b)
286
+ .map((d) => DAY_LABELS[d])
287
+ .join(",");
288
+
289
+ return (
290
+ <div className="flex flex-wrap items-center gap-2 text-sm" id={id}>
291
+ <span className="text-muted-foreground">Every</span>
292
+
293
+ {(freq === "minute" || freq === "hour") && (
294
+ <Select
295
+ value={String(freq === "minute" ? parts.interval : parts.hourInterval)}
296
+ onValueChange={(v) =>
297
+ freq === "minute"
298
+ ? update("minute", { ...parts, interval: Number(v) })
299
+ : update("hour", { ...parts, hourInterval: Number(v) })
300
+ }
301
+ disabled={isDisabled}
302
+ >
303
+ <SelectTrigger className="w-auto min-w-[4rem]">
304
+ <SelectValue />
305
+ </SelectTrigger>
306
+ <SelectContent className="max-h-60">
307
+ {(freq === "minute"
308
+ ? intervalOptions(parts.interval, MINUTE_INTERVAL_PRESETS)
309
+ : intervalOptions(parts.hourInterval, HOUR_INTERVAL_PRESETS)
310
+ ).map((n) => (
311
+ <SelectItem key={n} value={String(n)}>
312
+ {n}
313
+ </SelectItem>
314
+ ))}
315
+ </SelectContent>
316
+ </Select>
317
+ )}
318
+
319
+ <Select
320
+ value={freq}
321
+ onValueChange={(v) => setFreq(v as Frequency)}
322
+ disabled={isDisabled}
323
+ >
324
+ <SelectTrigger className="w-auto min-w-[5rem]">
325
+ <SelectValue />
326
+ </SelectTrigger>
327
+ <SelectContent>
328
+ {FREQUENCY_OPTIONS.map((option) => (
329
+ <SelectItem key={option.value} value={option.value}>
330
+ {option.label}
331
+ </SelectItem>
332
+ ))}
333
+ </SelectContent>
334
+ </Select>
335
+
336
+ {freq === "week" && (
337
+ <>
338
+ <span className="text-muted-foreground">on</span>
339
+ <DropdownMenu>
340
+ <DropdownMenuTrigger
341
+ disabled={isDisabled}
342
+ className={cn(
343
+ "flex h-9 min-w-[6rem] items-center justify-between gap-2 whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
344
+ )}
345
+ >
346
+ <span className={cn(parts.daysOfWeek.length === 0 && "text-muted-foreground")}>
347
+ {selectedDaysLabel}
348
+ </span>
349
+ <ChevronDown className="h-4 w-4 opacity-50" />
350
+ </DropdownMenuTrigger>
351
+ <DropdownMenuContent align="start">
352
+ {DAY_LABELS.map((label, day) => (
353
+ <DropdownMenuCheckboxItem
354
+ key={label}
355
+ checked={parts.daysOfWeek.includes(day)}
356
+ onCheckedChange={(checked) => toggleDay(day, checked)}
357
+ onSelect={(e) => e.preventDefault()}
358
+ >
359
+ {label}
360
+ </DropdownMenuCheckboxItem>
361
+ ))}
362
+ </DropdownMenuContent>
363
+ </DropdownMenu>
364
+ </>
365
+ )}
366
+
367
+ {freq === "month" && (
368
+ <>
369
+ <span className="text-muted-foreground">on day</span>
370
+ <UnitSelect
371
+ value={parts.dayOfMonth}
372
+ max={31}
373
+ start={1}
374
+ disabled={isDisabled}
375
+ onChange={(dayOfMonth) => update("month", { ...parts, dayOfMonth })}
376
+ />
377
+ </>
378
+ )}
379
+
380
+ {(freq === "day" || freq === "week" || freq === "month") && (
381
+ <>
382
+ <span className="text-muted-foreground">at</span>
383
+ <UnitSelect
384
+ value={parts.hour}
385
+ max={23}
386
+ format={pad}
387
+ disabled={isDisabled}
388
+ onChange={(hour) => update(freq, { ...parts, hour })}
389
+ />
390
+ <span className="text-muted-foreground">:</span>
391
+ <UnitSelect
392
+ value={parts.minute}
393
+ max={59}
394
+ format={pad}
395
+ disabled={isDisabled}
396
+ onChange={(minute) => update(freq, { ...parts, minute })}
397
+ />
398
+ </>
399
+ )}
400
+
401
+ {freq === "hour" && (
402
+ <>
403
+ <span className="text-muted-foreground">at minute</span>
404
+ <UnitSelect
405
+ value={parts.minute}
406
+ max={59}
407
+ format={pad}
408
+ disabled={isDisabled}
409
+ onChange={(minute) => update("hour", { ...parts, minute })}
410
+ />
411
+ </>
412
+ )}
413
+ </div>
414
+ );
415
+ }
416
+
417
+ type CronWidgetMap = { cron: typeof CronWidget };
418
+
419
+ export const cronWidgets: CronWidgetMap = { cron: CronWidget };
420
+
421
+ export { CronWidget, parseCron, buildCron, canonicalCron, roundTrips };
@@ -1,10 +1,12 @@
1
1
  import { RegistryWidgetsType } from "@rjsf/utils";
2
2
  import validator from "@rjsf/validator-ajv8";
3
3
  import { basicWidgets } from "./rjsf-basic-widgets";
4
+ import { cronWidgets } from "./rjsf-cron-widget";
4
5
  import { customTemplates } from "./rjsf-templates";
5
6
 
6
7
  export const customWidgets = {
7
8
  ...basicWidgets,
9
+ ...cronWidgets,
8
10
  } satisfies RegistryWidgetsType;
9
11
 
10
12
  export { customTemplates, validator };