lula2 0.3.1 → 0.3.2-nightly.0

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 (43) hide show
  1. package/dist/_app/immutable/assets/0.CLKu6Q8_.css +1 -0
  2. package/dist/_app/immutable/chunks/0xMSlW4Y.js +65 -0
  3. package/dist/_app/immutable/chunks/{BtOhWAVU.js → 152nb-LI.js} +2 -2
  4. package/dist/_app/immutable/chunks/{C5zWTfmV.js → 1spjHGNy.js} +1 -1
  5. package/dist/_app/immutable/chunks/{DnJ0bPgj.js → BtuEtkd3.js} +1 -1
  6. package/dist/_app/immutable/chunks/C113Bo4B.js +2 -0
  7. package/dist/_app/immutable/chunks/{CoF2vljD.js → CNOPXlDW.js} +1 -1
  8. package/dist/_app/immutable/chunks/{DqsOU3kV.js → DY3-lqhI.js} +1 -1
  9. package/dist/_app/immutable/chunks/{Ds14DLx0.js → DrOkR2YZ.js} +3 -3
  10. package/dist/_app/immutable/chunks/rsJnd9Tf.js +1 -0
  11. package/dist/_app/immutable/entry/{app.XLGRlmCF.js → app.CqP__jbe.js} +2 -2
  12. package/dist/_app/immutable/entry/start.C1hW6Z_g.js +1 -0
  13. package/dist/_app/immutable/nodes/{0.B6W16O68.js → 0.BWuQSqPo.js} +1 -1
  14. package/dist/_app/immutable/nodes/{1.wIilWkgu.js → 1.COMBeJ1R.js} +1 -1
  15. package/dist/_app/immutable/nodes/{2.CG53uQH9.js → 2.6bQMjmMR.js} +1 -1
  16. package/dist/_app/immutable/nodes/{3.D4uH9LCp.js → 3.BBPnkpxM.js} +1 -1
  17. package/dist/_app/immutable/nodes/{4.sYW2-VhJ.js → 4.CGIAObAE.js} +1 -1
  18. package/dist/_app/version.json +1 -1
  19. package/dist/cli/commands/ui.js +6 -6
  20. package/dist/cli/server/index.js +6 -6
  21. package/dist/cli/server/server.js +6 -6
  22. package/dist/cli/server/serverState.js +2 -2
  23. package/dist/cli/server/spreadsheetRoutes.js +4 -4
  24. package/dist/cli/server/websocketServer.js +6 -6
  25. package/dist/index.html +10 -10
  26. package/dist/index.js +6 -6
  27. package/package.json +21 -21
  28. package/src/lib/actions/clickOutside.ts +24 -0
  29. package/src/lib/components/controls/ControlsList.svelte +101 -75
  30. package/src/lib/components/controls/tabs/CustomFieldsTab.svelte +1 -1
  31. package/src/lib/components/controls/tabs/TimelineTab.svelte +1 -1
  32. package/src/lib/components/controls/utils/ProcessedTextRenderer.svelte +1 -1
  33. package/src/lib/components/forms/DynamicControlForm.svelte +2 -2
  34. package/src/lib/components/setup/ExistingControlSets.svelte +0 -1
  35. package/src/lib/components/setup/SpreadsheetImport.svelte +10 -10
  36. package/src/lib/components/ui/CustomDropdown.svelte +96 -0
  37. package/src/lib/components/ui/FilterBuilder.svelte +415 -0
  38. package/src/stores/compliance.ts +149 -39
  39. package/dist/_app/immutable/assets/0.Dv98laBw.css +0 -1
  40. package/dist/_app/immutable/chunks/BW28MavF.js +0 -1
  41. package/dist/_app/immutable/chunks/Cby0Z7eP.js +0 -2
  42. package/dist/_app/immutable/chunks/DQAmyY_z.js +0 -66
  43. package/dist/_app/immutable/entry/start.68S9ad6U.js +0 -1
@@ -1,4 +1,4 @@
1
- import"../chunks/DsnmJJEf.js";import{i as Ye}from"../chunks/C5zWTfmV.js";import{p as We,ae as Oe,d as o,T as C,G as se,h as e,af as nr,ag as Be,c as a,n as Me,r,b as v,e as Xe,J as H,K as I,ah as Nr,f as y,s as i,k as _,ai as g,a as we,aj as wr,ak as Fe,a0 as Ae,al as kr,am as sr,an as Cr,o as lt,W as st}from"../chunks/Cby0Z7eP.js";import{l as ir,p as Je,i as A,a as it,s as nt}from"../chunks/DqsOU3kV.js";import{a as Br,s as le,r as jr,e as me,i as De,b as Ne,c as Lr,d as Sr,f as Dr,w as $r}from"../chunks/BtOhWAVU.js";import{g as dt}from"../chunks/Ds14DLx0.js";var vt=Be("<title> </title>"),gt=Be('<svg><!><path d="M11 18L12.41 19.41 15 16.83 15 29 17 29 17 16.83 19.59 19.41 21 18 16 13 11 18z"></path><path d="M23.5,22H23V20h.5a4.5,4.5,0,0,0,.36-9L23,11l-.1-.82a7,7,0,0,0-13.88,0L9,11,8.14,11a4.5,4.5,0,0,0,.36,9H9v2H8.5A6.5,6.5,0,0,1,7.2,9.14a9,9,0,0,1,17.6,0A6.5,6.5,0,0,1,23.5,22Z"></path></svg>');function ct(he,G){const E=ir(G,["children","$$slots","$$events","$$legacy"]),de=ir(E,["size","title"]);We(G,!1);const T=C(),V=C();let B=Je(G,"size",8,16),w=Je(G,"title",8,void 0);Oe(()=>(se(E),se(w())),()=>{o(T,E["aria-label"]||E["aria-labelledby"]||w())}),Oe(()=>(e(T),se(E)),()=>{o(V,{"aria-hidden":e(T)?void 0:!0,role:e(T)?"img":void 0,focusable:Number(E.tabindex)===0?!0:void 0})}),nr(),Ye();var h=gt();Br(h,()=>({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",fill:"currentColor",preserveAspectRatio:"xMidYMid meet",width:B(),height:B(),...e(V),...de}));var S=a(h);{var be=J=>{var F=vt(),c=a(F,!0);r(F),H(()=>I(c,w())),v(J,F)};A(S,J=>{w()&&J(be)})}Me(2),r(h),v(he,h),Xe()}var pt=Be("<title> </title>"),ut=Be('<svg><!><path d="M10 6H14V10H10zM18 6H22V10H18zM10 14H14V18H10zM18 14H22V18H18zM10 22H14V26H10zM18 22H22V26H18z"></path></svg>');function lr(he,G){const E=ir(G,["children","$$slots","$$events","$$legacy"]),de=ir(E,["size","title"]);We(G,!1);const T=C(),V=C();let B=Je(G,"size",8,16),w=Je(G,"title",8,void 0);Oe(()=>(se(E),se(w())),()=>{o(T,E["aria-label"]||E["aria-labelledby"]||w())}),Oe(()=>(e(T),se(E)),()=>{o(V,{"aria-hidden":e(T)?void 0:!0,role:e(T)?"img":void 0,focusable:Number(E.tabindex)===0?!0:void 0})}),nr(),Ye();var h=ut();Br(h,()=>({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",fill:"currentColor",preserveAspectRatio:"xMidYMid meet",width:B(),height:B(),...e(V),...de}));var S=a(h);{var be=J=>{var F=pt(),c=a(F,!0);r(F),H(()=>I(c,w())),v(J,F)};A(S,J=>{w()&&J(be)})}Me(),r(h),v(he,h),Xe()}var bt=y('<div class="p-4 text-sm text-blue-800 rounded-lg bg-blue-50 dark:bg-gray-800 dark:text-blue-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <div><span class="font-medium">File loaded:</span> <div class="mt-1"><span class="font-medium">Sheets:</span> <span class="font-medium">Fields:</span> <span class="font-medium">Controls found:</span> </div></div></div></div>'),ft=y('<div class="p-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <span> </span></div></div>'),xt=y('<div class="p-4 text-sm text-green-800 rounded-lg bg-green-50 dark:bg-gray-800 dark:text-green-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <span> </span></div></div>'),mt=y("<option> </option>"),ht=y("<option> </option>"),yt=y("<option> </option>"),_t=y('<span class="ml-auto text-xs text-blue-600 dark:text-blue-400">ID</span>'),wt=y('<div draggable="true" role="button" tabindex="0" class="flex items-center px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 rounded text-sm cursor-move hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors opacity-75"><svg class="w-3 h-3 mr-2 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"><path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z"></path></svg> <span class="truncate line-through"> </span> <!></div>'),kt=y('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">No excluded fields</p>'),Ct=y('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),St=y('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),$t=y('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),Dt=y('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),Mt=y('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),zt=y('<div role="region" aria-label="Justification field drop zone"><p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p></div>'),At=y('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),Ft=y('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),Ot=y('<th class="px-4 py-2"> </th>'),Ht=y('<td class="px-4 py-2"> </td>'),Pt=y('<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700"></tr>'),Et=y('<div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Sample Data Preview</h3> <div class="overflow-x-auto"><table class="w-full text-sm text-left text-gray-500 dark:text-gray-400"><thead class="text-xs text-gray-700 uppercase bg-gray-100 dark:bg-gray-600 dark:text-gray-400"><tr></tr></thead><tbody></tbody></table></div></div>'),It=y('<span class="flex items-center"><svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> Importing...</span>'),Tt=y(`<div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Import Options</h3> <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"><div><label for="controlSetName" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Control Set Name <span class="text-red-500">*</span></label> <input type="text" id="controlSetName" placeholder="e.g., NIST 800-53 Rev 4" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white" required/> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">This will be used as the display name and folder name</p></div> <div><label for="controlSetDescription" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Description</label> <input type="text" id="controlSetDescription" placeholder="Optional description" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"/> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Brief description of this control set</p></div></div> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"><div><label for="sheet" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Sheet</label> <select id="sheet" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Select which worksheet contains your control data</p></div> <div><label for="headerRow" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Select Header Row</label> <select id="headerRow" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Select the row containing column headers</p></div> <div><label for="controlIdField" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Control ID Field <span class="text-red-500">*</span></label> <select id="controlIdField" required><option disabled>Select Control ID field</option><!></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Column containing unique control identifiers (e.g., AC-1, SC-7)</p></div></div></div> <div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">Organize Fields</h3> <p class="text-sm text-gray-600 dark:text-gray-400 mb-4">Drag fields to organize them. <strong>Overview fields</strong> will appear as table columns in
1
+ import"../chunks/DsnmJJEf.js";import{i as Ye}from"../chunks/1spjHGNy.js";import{p as We,ae as Oe,d as o,T as C,G as se,h as e,af as nr,ag as Be,c as a,n as Me,r,b as v,e as Xe,J as H,K as I,ah as Nr,f as y,s as i,k as _,ai as g,a as we,aj as wr,ak as Fe,a0 as Ae,al as kr,am as sr,an as Cr,o as lt,W as st}from"../chunks/C113Bo4B.js";import{l as ir,p as Je,i as A,a as it,s as nt}from"../chunks/DY3-lqhI.js";import{a as Br,s as le,r as jr,e as me,i as De,b as Ne,c as Lr,d as Sr,f as Dr,w as $r}from"../chunks/152nb-LI.js";import{g as dt}from"../chunks/DrOkR2YZ.js";var vt=Be("<title> </title>"),gt=Be('<svg><!><path d="M11 18L12.41 19.41 15 16.83 15 29 17 29 17 16.83 19.59 19.41 21 18 16 13 11 18z"></path><path d="M23.5,22H23V20h.5a4.5,4.5,0,0,0,.36-9L23,11l-.1-.82a7,7,0,0,0-13.88,0L9,11,8.14,11a4.5,4.5,0,0,0,.36,9H9v2H8.5A6.5,6.5,0,0,1,7.2,9.14a9,9,0,0,1,17.6,0A6.5,6.5,0,0,1,23.5,22Z"></path></svg>');function ct(he,G){const E=ir(G,["children","$$slots","$$events","$$legacy"]),de=ir(E,["size","title"]);We(G,!1);const T=C(),V=C();let B=Je(G,"size",8,16),w=Je(G,"title",8,void 0);Oe(()=>(se(E),se(w())),()=>{o(T,E["aria-label"]||E["aria-labelledby"]||w())}),Oe(()=>(e(T),se(E)),()=>{o(V,{"aria-hidden":e(T)?void 0:!0,role:e(T)?"img":void 0,focusable:Number(E.tabindex)===0?!0:void 0})}),nr(),Ye();var h=gt();Br(h,()=>({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",fill:"currentColor",preserveAspectRatio:"xMidYMid meet",width:B(),height:B(),...e(V),...de}));var S=a(h);{var be=J=>{var F=vt(),c=a(F,!0);r(F),H(()=>I(c,w())),v(J,F)};A(S,J=>{w()&&J(be)})}Me(2),r(h),v(he,h),Xe()}var pt=Be("<title> </title>"),ut=Be('<svg><!><path d="M10 6H14V10H10zM18 6H22V10H18zM10 14H14V18H10zM18 14H22V18H18zM10 22H14V26H10zM18 22H22V26H18z"></path></svg>');function lr(he,G){const E=ir(G,["children","$$slots","$$events","$$legacy"]),de=ir(E,["size","title"]);We(G,!1);const T=C(),V=C();let B=Je(G,"size",8,16),w=Je(G,"title",8,void 0);Oe(()=>(se(E),se(w())),()=>{o(T,E["aria-label"]||E["aria-labelledby"]||w())}),Oe(()=>(e(T),se(E)),()=>{o(V,{"aria-hidden":e(T)?void 0:!0,role:e(T)?"img":void 0,focusable:Number(E.tabindex)===0?!0:void 0})}),nr(),Ye();var h=ut();Br(h,()=>({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",fill:"currentColor",preserveAspectRatio:"xMidYMid meet",width:B(),height:B(),...e(V),...de}));var S=a(h);{var be=J=>{var F=pt(),c=a(F,!0);r(F),H(()=>I(c,w())),v(J,F)};A(S,J=>{w()&&J(be)})}Me(),r(h),v(he,h),Xe()}var bt=y('<div class="p-4 text-sm text-blue-800 rounded-lg bg-blue-50 dark:bg-gray-800 dark:text-blue-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <div><span class="font-medium">File loaded:</span> <div class="mt-1"><span class="font-medium">Sheets:</span> <span class="font-medium">Fields:</span> <span class="font-medium">Controls found:</span> </div></div></div></div>'),ft=y('<div class="p-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <span> </span></div></div>'),xt=y('<div class="p-4 text-sm text-green-800 rounded-lg bg-green-50 dark:bg-gray-800 dark:text-green-400"><div class="flex items-center"><svg class="flex-shrink-0 inline w-4 h-4 mr-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"></path></svg> <span> </span></div></div>'),mt=y("<option> </option>"),ht=y("<option> </option>"),yt=y("<option> </option>"),_t=y('<span class="ml-auto text-xs text-blue-600 dark:text-blue-400">ID</span>'),wt=y('<div draggable="true" role="button" tabindex="0" class="flex items-center px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 rounded text-sm cursor-move hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors opacity-75"><svg class="w-3 h-3 mr-2 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"><path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z"></path></svg> <span class="truncate line-through"> </span> <!></div>'),kt=y('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">No excluded fields</p>'),Ct=y('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),St=y('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),$t=y('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),Dt=y('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),Mt=y('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),zt=y('<div role="region" aria-label="Justification field drop zone"><p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p></div>'),At=y('<div draggable="true" role="button" tabindex="0"><!> <span class="truncate"> </span></div>'),Ft=y('<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">Drop fields here</p>'),Ot=y('<th class="px-4 py-2"> </th>'),Ht=y('<td class="px-4 py-2"> </td>'),Pt=y('<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700"></tr>'),Et=y('<div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Sample Data Preview</h3> <div class="overflow-x-auto"><table class="w-full text-sm text-left text-gray-500 dark:text-gray-400"><thead class="text-xs text-gray-700 uppercase bg-gray-100 dark:bg-gray-600 dark:text-gray-400"><tr></tr></thead><tbody></tbody></table></div></div>'),It=y('<span class="flex items-center"><svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> Importing...</span>'),Tt=y(`<div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Import Options</h3> <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"><div><label for="controlSetName" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Control Set Name <span class="text-red-500">*</span></label> <input type="text" id="controlSetName" placeholder="e.g., NIST 800-53 Rev 4" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white" required/> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">This will be used as the display name and folder name</p></div> <div><label for="controlSetDescription" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Description</label> <input type="text" id="controlSetDescription" placeholder="Optional description" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"/> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Brief description of this control set</p></div></div> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"><div><label for="sheet" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Sheet</label> <select id="sheet" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Select which worksheet contains your control data</p></div> <div><label for="headerRow" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Select Header Row</label> <select id="headerRow" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:text-white"></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Select the row containing column headers</p></div> <div><label for="controlIdField" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Control ID Field <span class="text-red-500">*</span></label> <select id="controlIdField" required><option disabled>Select Control ID field</option><!></select> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Column containing unique control identifiers (e.g., AC-1, SC-7)</p></div></div></div> <div class="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-700"><h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">Organize Fields</h3> <p class="text-sm text-gray-600 dark:text-gray-400 mb-4">Drag fields to organize them. <strong>Overview fields</strong> will appear as table columns in
2
2
  the controls list.</p> <div class="grid grid-cols-1 lg:grid-cols-5 gap-4"><div class="border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700 rounded-t-lg"><h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300">Excluded Fields</h4> <p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Not imported</p></div> <div role="region" aria-label="Excluded fields drop zone"><!> <!></div></div> <div class="border border-blue-300 dark:border-blue-700 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-blue-200 dark:border-blue-800 bg-blue-50 dark:bg-blue-900/20 rounded-t-lg"><h4 class="text-sm font-semibold text-blue-700 dark:text-blue-300">Overview Tab</h4> <p class="text-xs text-blue-600 dark:text-blue-400 mt-1">Shows in details & table columns</p></div> <div role="region" aria-label="Overview tab drop zone"><!> <!></div></div> <div class="border border-green-300 dark:border-green-700 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-green-200 dark:border-green-800 bg-green-50 dark:bg-green-900/20 rounded-t-lg"><h4 class="text-sm font-semibold text-green-700 dark:text-green-300">Implementation Tab</h4> <p class="text-xs text-green-600 dark:text-green-400 mt-1">Status & compliance</p></div> <div role="region" aria-label="Implementation tab drop zone"><!> <!></div></div> <div class="border border-orange-300 dark:border-orange-700 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-orange-200 dark:border-orange-800 bg-orange-50 dark:bg-orange-900/20 rounded-t-lg"><h4 class="text-sm font-semibold text-orange-700 dark:text-orange-300">Mappings Tab</h4> <p class="text-xs text-orange-600 dark:text-orange-400 mt-1">Pre-populate justification for a control mapping</p></div> <div role="region" aria-label="Justifications tab drop zone"><div class="space-y-2"><!></div></div></div> <div class="border border-purple-300 dark:border-purple-700 rounded-lg bg-white dark:bg-gray-800"><div class="p-3 border-b border-purple-200 dark:border-purple-800 bg-purple-50 dark:bg-purple-900/20 rounded-t-lg"><h4 class="text-sm font-semibold text-purple-700 dark:text-purple-300">Custom Tab</h4> <p class="text-xs text-purple-600 dark:text-purple-400 mt-1">Additional fields</p></div> <div role="region" aria-label="Custom fields drop zone"><!> <!></div></div></div></div> <!> <div class="flex justify-center"><button class="px-5 py-2.5 text-white bg-blue-600 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm disabled:opacity-50 disabled:cursor-not-allowed"><!></button></div>`,1),Vt=y('<div class="space-y-6"><div role="button" tabindex="0" class="relative"><label><div class="flex flex-col items-center justify-center pt-5 pb-6"><!> <p class="mb-2 text-sm text-gray-500 dark:text-gray-400"><span class="font-semibold">Click to upload</span> or drag and drop</p> <p class="text-xs text-gray-500 dark:text-gray-400">XLSX, XLS or CSV files</p></div> <input type="file" class="hidden" accept=".xlsx,.xls,.csv"/></label></div> <!> <!> <!> <!></div>');function jt(he,G){We(G,!1);const E=C(),de=Nr();let T=C(null),V=C(""),B=C(""),w=C([]),h=C([]),S=C([]),be=C(0),J=C([]),F=C([]),c=C(new Map),fe=C(1),R=C(""),Z=C(""),ie=C(""),Q=C(!1),ne=C(""),xe=C(""),ve=C(!1),ge=C(!1),$=C(null),W=C(null),ee=C(null);function u(){o(h,[]),o(S,[]),o(be,0),e(c).clear(),o(c,new Map),o(F,[]),o(R,""),o(ne,""),o(xe,""),o($,null),o(W,null),o(ee,null)}function b(l){l.preventDefault(),o(ge,!0)}function D(){o(ge,!1)}function L(l){l.preventDefault(),o(ge,!1);const n=l.dataTransfer?.files;n&&n.length>0&&Y(n[0])}function j(l){const n=l.target;n.files&&n.files.length>0&&Y(n.files[0])}async function Y(l){u(),o(V,l.name),o(T,l),o(ne,""),o(Q,!0),o(Z,e(V).replace(/\.[^.]+$/,"").replace(/[-_]/g," ")),o(ie,`Imported from ${e(V)}`);try{const n=new FormData;n.append("file",l);const z=await fetch("/api/parse-excel",{method:"POST",body:n});if(!z.ok){const x=await z.json();throw new Error(x.error||"Failed to parse file")}const k=await z.json();o(w,k.sheets||[]),o(B,k.selectedSheet||e(w)[0]),o(J,k.rowPreviews||[]),e(J).length>0&&e(fe)===1&&o(fe,e(J)[0].row),await ce(),o(ve,!0)}catch(n){o(ne,"Error reading file: "+n.message)}finally{o(Q,!1)}}async function ce(){if(!(!e(T)||!e(B))){o(Q,!0),e(c).clear(),o(c,new Map),o(R,"");try{const l=new FormData;l.append("file",e(T)),l.append("sheetName",e(B)),l.append("headerRow",e(fe).toString());const n=await fetch("/api/parse-excel-sheet",{method:"POST",body:l});if(!n.ok){const k=await n.json();throw new Error(k.error||"Failed to parse sheet")}const z=await n.json();if(o(h,z.fields||[]),o(S,z.sampleData||[]),o(be,z.controlCount||0),e(h).forEach((k,x)=>{const p=k.toLowerCase();let N="custom",q="text";p.includes("implementation")||p.includes("status")||p.includes("narrative")||p.includes("guidance")?N="implementation":(p.includes("id")||p.includes("title")||p.includes("family")||p.includes("cci")||p.includes("control")||p.includes("acronym"))&&(N="overview"),p.includes("description")||p.includes("narrative")||p.includes("guidance")||p.includes("statement")?q="textarea":p.includes("status")||p.includes("type")||p.includes("designation")?q="select":p.includes("date")?q="date":(p.includes("count")||p.includes("number"))&&(q="number"),e(c).set(k,{originalName:k,tab:N,displayOrder:x,fieldType:q,required:p.includes("id")||p.includes("title")})}),o(c,e(c)),e(R)&&!e(h).includes(e(R))&&o(R,""),!e(R)&&e(h).includes("AP Acronym")){const k=!e(S).length||e(S).every(te=>!te["AP Acronym"]||String(te["AP Acronym"]).length<25),x=e(S).map(te=>te["AP Acronym"]).filter(te=>te!=null&&te!==""&&String(te).trim()!==""),p=new Set(x),N=!x.length||p.size===x.length,q=x.length>0;k&&N&&q&&o(R,"AP Acronym")}}catch(l){o(ne,"Error loading sheet data: "+l.message)}finally{o(Q,!1)}}}function He(l){if(!l)return l;let n=l.trim().replace(/\r?\n/g," ").replace(/\s+/g," ").trim();return ze(n)}function ze(l){return l.replace(/\W+/g," ").split(/ |\s/).map(n=>n.toLowerCase()).join("-")}function re(l,n){o($,n),l.dataTransfer&&(l.dataTransfer.effectAllowed="move",l.dataTransfer.setData("text/plain",n))}function M(){o($,null),o(W,null),o(ee,null)}function pe(l,n){l.preventDefault(),o(W,n),l.dataTransfer&&(l.dataTransfer.dropEffect="move")}function ue(){o(W,null)}function ye(l,n,z){if(l.preventDefault(),e($)&&e(c).has(e($))){const k=e(c).get(e($));if(k.tab==="mappings"&&n!=="mappings"&&o(F,e(F).filter(x=>x!==e($))),n==="mappings"&&!e(F).includes(e($))&&o(F,[...e(F),e($)]),k.tab=n,z!==void 0&&n!==null){const p=Array.from(e(c).entries()).filter(([N,q])=>q.tab===n).sort((N,q)=>N[1].displayOrder-q[1].displayOrder).filter(([N])=>N!==e($));p.splice(z,0,[e($),k]),p.forEach(([N,q],te)=>{q.displayOrder=te,e(c).set(N,q)})}else if(n!==null){const x=Math.max(0,...Array.from(e(c).values()).filter(p=>p.tab===n).map(p=>p.displayOrder));k.displayOrder=x+1}e(c).set(e($),k),o(c,e(c))}o($,null),o(W,null)}function ke(l,n){l.preventDefault(),l.stopPropagation(),o(ee,n)}function Ce(){o(ee,null)}function Se(l,n,z){if(l.preventDefault(),l.stopPropagation(),e($)&&e($)!==n){const x=Array.from(e(c).entries()).filter(([p,N])=>N.tab===z).sort((p,N)=>p[1].displayOrder-N[1].displayOrder).findIndex(([p])=>p===n);x!==-1&&ye(l,z,x)}o(ee,null)}async function dr(){if(!(!e(T)||!e(V))){if(!e(R)){o(ne,"Please select a Control ID field before importing"),o(xe,"");return}if(!e(Z)||e(Z).trim()===""){o(ne,"Please enter a Control Set Name before importing"),o(xe,"");return}o(Q,!0),o(ne,""),o(xe,"");try{const l=new FormData;l.append("file",e(T),e(V)),l.append("controlIdField",e(R)),l.append("startRow",e(fe).toString()),l.append("namingConvention","kebab-case"),l.append("skipEmpty","true"),l.append("skipEmptyRows","true"),l.append("controlSetName",e(Z)||e(V).replace(/\.[^.]+$/,"")),l.append("controlSetDescription",e(ie)||`Imported from ${e(V)}`);const n=Array.from(e(c).entries()).filter(([x,p])=>p.tab!==null).map(([x,p])=>({fieldName:He(x),...p}));l.append("fieldSchema",JSON.stringify(n)),l.append("justificationFields",JSON.stringify(e(F).map(x=>He(x))));const z=await fetch("/api/import-spreadsheet",{method:"POST",body:l});if(!z.ok){const x=await z.json();throw new Error(x.error||"Import failed")}const k=await z.json();o(xe,`Successfully imported ${k.controlCount} controls into ${k.families.length} families`),de("created",{path:k.outputDir})}catch(l){o(ne,"Error importing spreadsheet: "+l.message)}finally{o(Q,!1)}}}Oe(()=>e(h),()=>{o(E,e(h).filter(l=>e(h).includes(l)))}),nr(),Ye();var Pe=Vt(),$e=a(Pe),Ee=a($e),Ze=a(Ee),Ke=a(Ze);ct(Ke,{class:"w-8 h-8 mb-4 text-gray-500 dark:text-gray-400"}),Me(4),r(Ze);var Re=i(Ze,2);r(Ee),r($e);var Ge=i($e,2);{var vr=l=>{var n=bt(),z=a(n),k=i(a(z),2),x=i(a(k)),p=i(x),N=i(a(p)),q=i(N,2),te=i(q,2);r(p),r(k),r(z),r(n),H(()=>{I(x,` ${e(V)??""} `),I(N,` ${e(w),_(()=>e(w).length)??""} | `),I(q,` ${e(h),_(()=>e(h).length)??""} | `),I(te,` ${e(be)??""}`)}),v(l,n)};A(Ge,l=>{e(V)&&l(vr)})}var Qe=i(Ge,2);{var er=l=>{var n=ft(),z=a(n),k=i(a(z),2),x=a(k,!0);r(k),r(z),r(n),H(()=>I(x,e(ne))),v(l,n)};A(Qe,l=>{e(ne)&&l(er)})}var rr=i(Qe,2);{var gr=l=>{var n=xt(),z=a(n),k=i(a(z),2),x=a(k,!0);r(k),r(z),r(n),H(()=>I(x,e(xe))),v(l,n)};A(rr,l=>{e(xe)&&l(gr)})}var cr=i(rr,2);{var X=l=>{var n=Tt(),z=we(n),k=i(a(z),2),x=a(k),p=i(a(x),2);jr(p),Me(2),r(x);var N=i(x,2),q=i(a(N),2);jr(q),Me(2),r(N),r(k);var te=i(k,2),pr=a(te),tr=i(a(pr),2);H(()=>{e(B),wr(()=>{e(w)})}),me(tr,5,()=>e(w),De,(t,s)=>{var f=mt(),m=a(f,!0);r(f);var d={};H(()=>{I(m,e(s)),d!==(d=e(s))&&(f.value=(f.__value=e(s))??"")}),v(t,f)}),r(tr),Me(2),r(pr);var ur=i(pr,2),ar=i(a(ur),2);H(()=>{e(fe),wr(()=>{e(J)})}),me(ar,5,()=>e(J),De,(t,s)=>{var f=ht(),m=a(f);r(f);var d={};H(()=>{I(m,`Row ${e(s),_(()=>e(s).row)??""}: ${e(s),_(()=>e(s).preview)??""}`),d!==(d=(e(s),_(()=>e(s).row)))&&(f.value=(f.__value=(e(s),_(()=>e(s).row)))??"")}),v(t,f)}),r(ar),Me(2),r(ur);var Mr=i(ur,2),or=i(a(Mr),2);H(()=>{e(R),wr(()=>{e(h),e(S)})});var br=a(or);br.value=br.__value="";var Zr=i(br);me(Zr,1,()=>e(h),De,(t,s)=>{const f=Ae(()=>(e(S),e(s),_(()=>e(S).length>0&&e(S)[0][e(s)]?String(e(S)[0][e(s)]).slice(0,30):""))),m=Ae(()=>(e(S),e(s),_(()=>!e(S).length||e(S).every(oe=>!oe[e(s)]||String(oe[e(s)]).length<25)))),d=Ae(()=>(e(S),e(s),_(()=>e(S).map(oe=>oe[e(s)]).filter(oe=>oe!=null&&oe!==""&&String(oe).trim()!=="")))),P=Ae(()=>new Set(e(d))),K=Ae(()=>(se(e(d)),se(e(P)),_(()=>!e(d).length||e(P).size===e(d).length))),U=Ae(()=>(se(e(d)),_(()=>e(d).length>0)));var O=Fe(),ae=we(O);{var Ue=oe=>{var _e=yt(),_r=a(_e);r(_e);var Vr={};H(()=>{I(_r,`${e(s)??""}${e(f)?` (e.g., ${e(f)})`:""}`),Vr!==(Vr=e(s))&&(_e.value=(_e.__value=e(s))??"")}),v(oe,_e)};A(ae,oe=>{e(m)&&e(K)&&e(U)&&oe(Ue)})}v(t,O)}),r(or),Me(2),r(Mr),r(te),r(z);var fr=i(z,2),zr=i(a(fr),4),xr=a(zr),Ie=i(a(xr),2),Ar=a(Ie);me(Ar,1,()=>(e(h),e(c),_(()=>e(h).filter(t=>!e(c).get(t)||e(c).get(t)?.tab===null))),t=>t,(t,s)=>{var f=wt(),m=i(a(f),2),d=a(m,!0);r(m);var P=i(m,2);{var K=U=>{var O=_t();v(U,O)};A(P,U=>{e(s)===e(R)&&U(K)})}r(f),H(()=>{Ne(f,"aria-label",`Drag ${e(s)??""} field`),I(d,e(s))}),g("dragstart",f,U=>re(U,e(s))),g("dragend",f,M),v(t,f)});var Rr=i(Ar,2);{var qr=t=>{var s=kt();v(t,s)};A(Rr,t=>{e(h),e(c),_(()=>e(h).filter(s=>!e(c).get(s)||e(c).get(s)?.tab===null).length===0)&&t(qr)})}r(Ie),r(xr);var mr=i(xr,2),Te=i(a(mr),2),Fr=a(Te);me(Fr,3,()=>(e(c),_(()=>Array.from(e(c).entries()).filter(([t,s])=>s.tab==="overview").sort((t,s)=>t[1].displayOrder-s[1].displayOrder))),([t,s])=>t,(t,s)=>{var f=kr(()=>Cr(e(s),2));let m=()=>e(f)[0];var d=Ct(),P=a(d);lr(P,{class:"w-3 h-3 mr-2 flex-shrink-0"});var K=i(P,2),U=a(K,!0);r(K),r(d),H(()=>{Ne(d,"aria-label",`${m()??""} field in Overview tab`),le(d,1,`flex items-center px-3 py-2 bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-300 rounded text-sm cursor-move hover:bg-blue-200 dark:hover:bg-blue-800/30 transition-colors
3
3
  ${e(ee)===m()&&e($)!==m()?"border-t-2 border-blue-500":""}`),I(U,m())}),g("dragstart",d,O=>re(O,m())),g("dragend",d,M),g("dragover",d,O=>ke(O,m())),g("dragleave",d,Ce),g("drop",d,O=>Se(O,m(),"overview")),v(t,d)});var Ur=i(Fr,2);{var Jr=t=>{var s=St();v(t,s)};A(Ur,t=>{e(c),_(()=>Array.from(e(c).entries()).filter(([s,f])=>f.tab==="overview").length===0)&&t(Jr)})}r(Te),r(mr);var hr=i(mr,2),Ve=i(a(hr),2),Or=a(Ve);me(Or,3,()=>(e(c),_(()=>Array.from(e(c).entries()).filter(([t,s])=>s.tab==="implementation").sort((t,s)=>t[1].displayOrder-s[1].displayOrder))),([t,s])=>t,(t,s)=>{var f=kr(()=>Cr(e(s),2));let m=()=>e(f)[0];var d=$t(),P=a(d);lr(P,{class:"w-3 h-3 mr-2 flex-shrink-0"});var K=i(P,2),U=a(K,!0);r(K),r(d),H(()=>{Ne(d,"aria-label",`${m()??""} field in Implementation tab`),le(d,1,`flex items-center px-3 py-2 bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-300 rounded text-sm cursor-move hover:bg-green-200 dark:hover:bg-green-800/30 transition-colors
4
4
  ${e(ee)===m()&&e($)!==m()?"border-t-2 border-green-500":""}`),I(U,m())}),g("dragstart",d,O=>re(O,m())),g("dragend",d,M),g("dragover",d,O=>ke(O,m())),g("dragleave",d,Ce),g("drop",d,O=>Se(O,m(),"implementation")),v(t,d)});var Yr=i(Or,2);{var Wr=t=>{var s=Dt();v(t,s)};A(Yr,t=>{e(c),_(()=>Array.from(e(c).entries()).filter(([s,f])=>f.tab==="implementation").length===0)&&t(Wr)})}r(Ve),r(hr);var yr=i(hr,2),je=i(a(yr),2),Hr=a(je),Xr=a(Hr);{var Kr=t=>{var s=Fe(),f=we(s);me(f,1,()=>e(F),De,(m,d)=>{var P=Mt(),K=a(P);lr(K,{class:"w-3 h-3 mr-2 flex-shrink-0"});var U=i(K,2),O=a(U,!0);r(U),r(P),H(()=>{Ne(P,"aria-label",`${e(d)??""} field in Mappings tab`),le(P,1,`flex items-center px-3 py-2 bg-orange-100 dark:bg-orange-900/30 text-orange-800 dark:text-orange-300 rounded text-sm cursor-move hover:bg-orange-200 dark:hover:bg-orange-800/30 transition-colors
@@ -1 +1 @@
1
- {"version":"1758141471553"}
1
+ {"version":"1758145813279"}
@@ -2561,7 +2561,7 @@ var init_gitHistory = __esm({
2561
2561
  changes = diffResult.changes;
2562
2562
  diff = diffResult.diff;
2563
2563
  yamlDiff = diffResult.yamlDiff;
2564
- } catch (error) {
2564
+ } catch {
2565
2565
  }
2566
2566
  }
2567
2567
  gitCommits.push({
@@ -2618,7 +2618,7 @@ var init_gitHistory = __esm({
2618
2618
  diff,
2619
2619
  yamlDiff
2620
2620
  };
2621
- } catch (error) {
2621
+ } catch {
2622
2622
  return { changes: { insertions: 0, deletions: 0, files: 1 } };
2623
2623
  }
2624
2624
  }
@@ -3293,7 +3293,7 @@ var init_spreadsheetRoutes = __esm({
3293
3293
  if (req.body.fieldSchema) {
3294
3294
  try {
3295
3295
  frontendFieldSchema = JSON.parse(req.body.fieldSchema);
3296
- } catch (e) {
3296
+ } catch {
3297
3297
  }
3298
3298
  }
3299
3299
  const fields = {};
@@ -3553,7 +3553,7 @@ var init_spreadsheetRoutes = __esm({
3553
3553
  if (!worksheet) {
3554
3554
  return res.status(400).json({ error: "No worksheet found in file" });
3555
3555
  }
3556
- worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {
3556
+ worksheet.eachRow({ includeEmpty: false }, (row, _rowNumber) => {
3557
3557
  const rowData = [];
3558
3558
  row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
3559
3559
  rowData[colNumber - 1] = cell.value;
@@ -3563,7 +3563,7 @@ var init_spreadsheetRoutes = __esm({
3563
3563
  }
3564
3564
  const headerCandidates = rows.slice(0, 5).map((row, index) => ({
3565
3565
  row: index + 1,
3566
- preview: row.slice(0, 4).filter((v) => v != null).join(", ") + (row.length > 4 ? ", ..." : "")
3566
+ preview: row.slice(0, 4).filter((v) => v !== null).filter((v) => v !== void 0).join(", ") + (row.length > 4 ? ", ..." : "")
3567
3567
  }));
3568
3568
  res.json({
3569
3569
  sheets,
@@ -3598,7 +3598,7 @@ var init_spreadsheetRoutes = __esm({
3598
3598
  if (!worksheet) {
3599
3599
  return res.status(400).json({ error: `Sheet "${sheetName}" not found` });
3600
3600
  }
3601
- worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {
3601
+ worksheet.eachRow({ includeEmpty: false }, (row, _rowNumber) => {
3602
3602
  const rowData = [];
3603
3603
  row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
3604
3604
  rowData[colNumber - 1] = cell.value;
@@ -2543,7 +2543,7 @@ var init_gitHistory = __esm({
2543
2543
  changes = diffResult.changes;
2544
2544
  diff = diffResult.diff;
2545
2545
  yamlDiff = diffResult.yamlDiff;
2546
- } catch (error) {
2546
+ } catch {
2547
2547
  }
2548
2548
  }
2549
2549
  gitCommits.push({
@@ -2600,7 +2600,7 @@ var init_gitHistory = __esm({
2600
2600
  diff,
2601
2601
  yamlDiff
2602
2602
  };
2603
- } catch (error) {
2603
+ } catch {
2604
2604
  return { changes: { insertions: 0, deletions: 0, files: 1 } };
2605
2605
  }
2606
2606
  }
@@ -3275,7 +3275,7 @@ var init_spreadsheetRoutes = __esm({
3275
3275
  if (req.body.fieldSchema) {
3276
3276
  try {
3277
3277
  frontendFieldSchema = JSON.parse(req.body.fieldSchema);
3278
- } catch (e) {
3278
+ } catch {
3279
3279
  }
3280
3280
  }
3281
3281
  const fields = {};
@@ -3535,7 +3535,7 @@ var init_spreadsheetRoutes = __esm({
3535
3535
  if (!worksheet) {
3536
3536
  return res.status(400).json({ error: "No worksheet found in file" });
3537
3537
  }
3538
- worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {
3538
+ worksheet.eachRow({ includeEmpty: false }, (row, _rowNumber) => {
3539
3539
  const rowData = [];
3540
3540
  row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
3541
3541
  rowData[colNumber - 1] = cell.value;
@@ -3545,7 +3545,7 @@ var init_spreadsheetRoutes = __esm({
3545
3545
  }
3546
3546
  const headerCandidates = rows.slice(0, 5).map((row, index) => ({
3547
3547
  row: index + 1,
3548
- preview: row.slice(0, 4).filter((v) => v != null).join(", ") + (row.length > 4 ? ", ..." : "")
3548
+ preview: row.slice(0, 4).filter((v) => v !== null).filter((v) => v !== void 0).join(", ") + (row.length > 4 ? ", ..." : "")
3549
3549
  }));
3550
3550
  res.json({
3551
3551
  sheets,
@@ -3580,7 +3580,7 @@ var init_spreadsheetRoutes = __esm({
3580
3580
  if (!worksheet) {
3581
3581
  return res.status(400).json({ error: `Sheet "${sheetName}" not found` });
3582
3582
  }
3583
- worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {
3583
+ worksheet.eachRow({ includeEmpty: false }, (row, _rowNumber) => {
3584
3584
  const rowData = [];
3585
3585
  row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
3586
3586
  rowData[colNumber - 1] = cell.value;
@@ -2543,7 +2543,7 @@ var init_gitHistory = __esm({
2543
2543
  changes = diffResult.changes;
2544
2544
  diff = diffResult.diff;
2545
2545
  yamlDiff = diffResult.yamlDiff;
2546
- } catch (error) {
2546
+ } catch {
2547
2547
  }
2548
2548
  }
2549
2549
  gitCommits.push({
@@ -2600,7 +2600,7 @@ var init_gitHistory = __esm({
2600
2600
  diff,
2601
2601
  yamlDiff
2602
2602
  };
2603
- } catch (error) {
2603
+ } catch {
2604
2604
  return { changes: { insertions: 0, deletions: 0, files: 1 } };
2605
2605
  }
2606
2606
  }
@@ -3275,7 +3275,7 @@ var init_spreadsheetRoutes = __esm({
3275
3275
  if (req.body.fieldSchema) {
3276
3276
  try {
3277
3277
  frontendFieldSchema = JSON.parse(req.body.fieldSchema);
3278
- } catch (e) {
3278
+ } catch {
3279
3279
  }
3280
3280
  }
3281
3281
  const fields = {};
@@ -3535,7 +3535,7 @@ var init_spreadsheetRoutes = __esm({
3535
3535
  if (!worksheet) {
3536
3536
  return res.status(400).json({ error: "No worksheet found in file" });
3537
3537
  }
3538
- worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {
3538
+ worksheet.eachRow({ includeEmpty: false }, (row, _rowNumber) => {
3539
3539
  const rowData = [];
3540
3540
  row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
3541
3541
  rowData[colNumber - 1] = cell.value;
@@ -3545,7 +3545,7 @@ var init_spreadsheetRoutes = __esm({
3545
3545
  }
3546
3546
  const headerCandidates = rows.slice(0, 5).map((row, index) => ({
3547
3547
  row: index + 1,
3548
- preview: row.slice(0, 4).filter((v) => v != null).join(", ") + (row.length > 4 ? ", ..." : "")
3548
+ preview: row.slice(0, 4).filter((v) => v !== null).filter((v) => v !== void 0).join(", ") + (row.length > 4 ? ", ..." : "")
3549
3549
  }));
3550
3550
  res.json({
3551
3551
  sheets,
@@ -3580,7 +3580,7 @@ var init_spreadsheetRoutes = __esm({
3580
3580
  if (!worksheet) {
3581
3581
  return res.status(400).json({ error: `Sheet "${sheetName}" not found` });
3582
3582
  }
3583
- worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {
3583
+ worksheet.eachRow({ includeEmpty: false }, (row, _rowNumber) => {
3584
3584
  const rowData = [];
3585
3585
  row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
3586
3586
  rowData[colNumber - 1] = cell.value;
@@ -950,7 +950,7 @@ var GitHistoryUtil = class {
950
950
  changes = diffResult.changes;
951
951
  diff = diffResult.diff;
952
952
  yamlDiff = diffResult.yamlDiff;
953
- } catch (error) {
953
+ } catch {
954
954
  }
955
955
  }
956
956
  gitCommits.push({
@@ -1007,7 +1007,7 @@ var GitHistoryUtil = class {
1007
1007
  diff,
1008
1008
  yamlDiff
1009
1009
  };
1010
- } catch (error) {
1010
+ } catch {
1011
1011
  return { changes: { insertions: 0, deletions: 0, files: 1 } };
1012
1012
  }
1013
1013
  }
@@ -234,7 +234,7 @@ router.post("/import-spreadsheet", upload.single("file"), async (req, res) => {
234
234
  if (req.body.fieldSchema) {
235
235
  try {
236
236
  frontendFieldSchema = JSON.parse(req.body.fieldSchema);
237
- } catch (e) {
237
+ } catch {
238
238
  }
239
239
  }
240
240
  const fields = {};
@@ -743,7 +743,7 @@ router.post("/parse-excel", upload.single("file"), async (req, res) => {
743
743
  if (!worksheet) {
744
744
  return res.status(400).json({ error: "No worksheet found in file" });
745
745
  }
746
- worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {
746
+ worksheet.eachRow({ includeEmpty: false }, (row, _rowNumber) => {
747
747
  const rowData = [];
748
748
  row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
749
749
  rowData[colNumber - 1] = cell.value;
@@ -753,7 +753,7 @@ router.post("/parse-excel", upload.single("file"), async (req, res) => {
753
753
  }
754
754
  const headerCandidates = rows.slice(0, 5).map((row, index) => ({
755
755
  row: index + 1,
756
- preview: row.slice(0, 4).filter((v) => v != null).join(", ") + (row.length > 4 ? ", ..." : "")
756
+ preview: row.slice(0, 4).filter((v) => v !== null).filter((v) => v !== void 0).join(", ") + (row.length > 4 ? ", ..." : "")
757
757
  }));
758
758
  res.json({
759
759
  sheets,
@@ -788,7 +788,7 @@ router.post("/parse-excel-sheet", upload.single("file"), async (req, res) => {
788
788
  if (!worksheet) {
789
789
  return res.status(400).json({ error: `Sheet "${sheetName}" not found` });
790
790
  }
791
- worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {
791
+ worksheet.eachRow({ includeEmpty: false }, (row, _rowNumber) => {
792
792
  const rowData = [];
793
793
  row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
794
794
  rowData[colNumber - 1] = cell.value;
@@ -990,7 +990,7 @@ var init_gitHistory = __esm({
990
990
  changes = diffResult.changes;
991
991
  diff = diffResult.diff;
992
992
  yamlDiff = diffResult.yamlDiff;
993
- } catch (error) {
993
+ } catch {
994
994
  }
995
995
  }
996
996
  gitCommits.push({
@@ -1047,7 +1047,7 @@ var init_gitHistory = __esm({
1047
1047
  diff,
1048
1048
  yamlDiff
1049
1049
  };
1050
- } catch (error) {
1050
+ } catch {
1051
1051
  return { changes: { insertions: 0, deletions: 0, files: 1 } };
1052
1052
  }
1053
1053
  }
@@ -1722,7 +1722,7 @@ var init_spreadsheetRoutes = __esm({
1722
1722
  if (req.body.fieldSchema) {
1723
1723
  try {
1724
1724
  frontendFieldSchema = JSON.parse(req.body.fieldSchema);
1725
- } catch (e) {
1725
+ } catch {
1726
1726
  }
1727
1727
  }
1728
1728
  const fields = {};
@@ -1982,7 +1982,7 @@ var init_spreadsheetRoutes = __esm({
1982
1982
  if (!worksheet) {
1983
1983
  return res.status(400).json({ error: "No worksheet found in file" });
1984
1984
  }
1985
- worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {
1985
+ worksheet.eachRow({ includeEmpty: false }, (row, _rowNumber) => {
1986
1986
  const rowData = [];
1987
1987
  row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
1988
1988
  rowData[colNumber - 1] = cell.value;
@@ -1992,7 +1992,7 @@ var init_spreadsheetRoutes = __esm({
1992
1992
  }
1993
1993
  const headerCandidates = rows.slice(0, 5).map((row, index) => ({
1994
1994
  row: index + 1,
1995
- preview: row.slice(0, 4).filter((v) => v != null).join(", ") + (row.length > 4 ? ", ..." : "")
1995
+ preview: row.slice(0, 4).filter((v) => v !== null).filter((v) => v !== void 0).join(", ") + (row.length > 4 ? ", ..." : "")
1996
1996
  }));
1997
1997
  res.json({
1998
1998
  sheets,
@@ -2027,7 +2027,7 @@ var init_spreadsheetRoutes = __esm({
2027
2027
  if (!worksheet) {
2028
2028
  return res.status(400).json({ error: `Sheet "${sheetName}" not found` });
2029
2029
  }
2030
- worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {
2030
+ worksheet.eachRow({ includeEmpty: false }, (row, _rowNumber) => {
2031
2031
  const rowData = [];
2032
2032
  row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
2033
2033
  rowData[colNumber - 1] = cell.value;
package/dist/index.html CHANGED
@@ -6,28 +6,28 @@
6
6
  <link rel="icon" href="/lula.png" />
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1" />
8
8
 
9
- <link rel="modulepreload" href="/_app/immutable/entry/start.68S9ad6U.js">
10
- <link rel="modulepreload" href="/_app/immutable/chunks/Ds14DLx0.js">
11
- <link rel="modulepreload" href="/_app/immutable/chunks/Cby0Z7eP.js">
12
- <link rel="modulepreload" href="/_app/immutable/entry/app.XLGRlmCF.js">
9
+ <link rel="modulepreload" href="/_app/immutable/entry/start.C1hW6Z_g.js">
10
+ <link rel="modulepreload" href="/_app/immutable/chunks/DrOkR2YZ.js">
11
+ <link rel="modulepreload" href="/_app/immutable/chunks/C113Bo4B.js">
12
+ <link rel="modulepreload" href="/_app/immutable/entry/app.CqP__jbe.js">
13
13
  <link rel="modulepreload" href="/_app/immutable/chunks/DsnmJJEf.js">
14
- <link rel="modulepreload" href="/_app/immutable/chunks/DqsOU3kV.js">
15
- <link rel="modulepreload" href="/_app/immutable/chunks/CoF2vljD.js">
16
- <link rel="modulepreload" href="/_app/immutable/chunks/DnJ0bPgj.js">
14
+ <link rel="modulepreload" href="/_app/immutable/chunks/DY3-lqhI.js">
15
+ <link rel="modulepreload" href="/_app/immutable/chunks/CNOPXlDW.js">
16
+ <link rel="modulepreload" href="/_app/immutable/chunks/BtuEtkd3.js">
17
17
  </head>
18
18
  <body data-sveltekit-preload-data="hover">
19
19
  <div style="display: contents">
20
20
  <script>
21
21
  {
22
- __sveltekit_6f9em3 = {
22
+ __sveltekit_u5u0c8 = {
23
23
  base: ""
24
24
  };
25
25
 
26
26
  const element = document.currentScript.parentElement;
27
27
 
28
28
  Promise.all([
29
- import("/_app/immutable/entry/start.68S9ad6U.js"),
30
- import("/_app/immutable/entry/app.XLGRlmCF.js")
29
+ import("/_app/immutable/entry/start.C1hW6Z_g.js"),
30
+ import("/_app/immutable/entry/app.CqP__jbe.js")
31
31
  ]).then(([kit, app]) => {
32
32
  kit.start(app, element);
33
33
  });
package/dist/index.js CHANGED
@@ -2562,7 +2562,7 @@ var init_gitHistory = __esm({
2562
2562
  changes = diffResult.changes;
2563
2563
  diff = diffResult.diff;
2564
2564
  yamlDiff = diffResult.yamlDiff;
2565
- } catch (error) {
2565
+ } catch {
2566
2566
  }
2567
2567
  }
2568
2568
  gitCommits.push({
@@ -2619,7 +2619,7 @@ var init_gitHistory = __esm({
2619
2619
  diff,
2620
2620
  yamlDiff
2621
2621
  };
2622
- } catch (error) {
2622
+ } catch {
2623
2623
  return { changes: { insertions: 0, deletions: 0, files: 1 } };
2624
2624
  }
2625
2625
  }
@@ -3294,7 +3294,7 @@ var init_spreadsheetRoutes = __esm({
3294
3294
  if (req.body.fieldSchema) {
3295
3295
  try {
3296
3296
  frontendFieldSchema = JSON.parse(req.body.fieldSchema);
3297
- } catch (e) {
3297
+ } catch {
3298
3298
  }
3299
3299
  }
3300
3300
  const fields = {};
@@ -3554,7 +3554,7 @@ var init_spreadsheetRoutes = __esm({
3554
3554
  if (!worksheet) {
3555
3555
  return res.status(400).json({ error: "No worksheet found in file" });
3556
3556
  }
3557
- worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {
3557
+ worksheet.eachRow({ includeEmpty: false }, (row, _rowNumber) => {
3558
3558
  const rowData = [];
3559
3559
  row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
3560
3560
  rowData[colNumber - 1] = cell.value;
@@ -3564,7 +3564,7 @@ var init_spreadsheetRoutes = __esm({
3564
3564
  }
3565
3565
  const headerCandidates = rows.slice(0, 5).map((row, index) => ({
3566
3566
  row: index + 1,
3567
- preview: row.slice(0, 4).filter((v) => v != null).join(", ") + (row.length > 4 ? ", ..." : "")
3567
+ preview: row.slice(0, 4).filter((v) => v !== null).filter((v) => v !== void 0).join(", ") + (row.length > 4 ? ", ..." : "")
3568
3568
  }));
3569
3569
  res.json({
3570
3570
  sheets,
@@ -3599,7 +3599,7 @@ var init_spreadsheetRoutes = __esm({
3599
3599
  if (!worksheet) {
3600
3600
  return res.status(400).json({ error: `Sheet "${sheetName}" not found` });
3601
3601
  }
3602
- worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {
3602
+ worksheet.eachRow({ includeEmpty: false }, (row, _rowNumber) => {
3603
3603
  const rowData = [];
3604
3604
  row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
3605
3605
  rowData[colNumber - 1] = cell.value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lula2",
3
- "version": "0.3.1",
3
+ "version": "0.3.2-nightly.0",
4
4
  "description": "A tool for managing compliance as code in your GitHub repositories.",
5
5
  "bin": {
6
6
  "lula2": "./dist/lula2"
@@ -33,25 +33,6 @@
33
33
  "!dist/**/*.test.js*",
34
34
  "!dist/**/*.test.d.ts*"
35
35
  ],
36
- "scripts": {
37
- "dev": "vite dev --port 5173",
38
- "dev:api": "tsx --watch index.ts --debug ui --port 3000 --no-open-browser",
39
- "dev:full": "concurrently \"npm run dev:api\" \"npm run dev\"",
40
- "build": "npm run build:svelte && npm run build:cli && npm run postbuild:cli",
41
- "build:svelte": "vite build",
42
- "build:cli": "esbuild index.ts cli/**/*.ts --bundle --platform=node --target=node22 --format=esm --outdir=dist --external:express --external:commander --external:js-yaml --external:yaml --external:isomorphic-git --external:glob --external:open --external:ws --external:cors --external:multer --external:@octokit/rest --external:undici --external:exceljs --external:csv-parse",
43
- "postbuild:cli": "cp cli-wrapper.mjs dist/lula2 && chmod +x dist/lula2",
44
- "preview": "vite preview",
45
- "prepare": "svelte-kit sync || echo ''",
46
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && tsc --noEmit",
47
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
48
- "format": "prettier --write 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
49
- "format:check": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
50
- "lint": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts' && eslint src cli",
51
- "test": "npm run test:unit -- --run --coverage",
52
- "test:integration": "vitest --config integration/vitest.config.integration.ts",
53
- "test:unit": "vitest"
54
- },
55
36
  "dependencies": {
56
37
  "@octokit/rest": "^22.0.0",
57
38
  "@types/ws": "^8.18.1",
@@ -75,6 +56,7 @@
75
56
  "@commitlint/cli": "^19.8.1",
76
57
  "@commitlint/config-conventional": "^19.8.1",
77
58
  "@eslint/compat": "^1.3.2",
59
+ "@eslint/eslintrc": "^3.3.1",
78
60
  "@eslint/js": "^9.35.0",
79
61
  "@playwright/test": "^1.55.0",
80
62
  "@sveltejs/adapter-static": "^3.0.9",
@@ -123,5 +105,23 @@
123
105
  "main",
124
106
  "next"
125
107
  ]
108
+ },
109
+ "scripts": {
110
+ "dev": "vite dev --port 5173",
111
+ "dev:api": "tsx --watch index.ts --debug ui --port 3000 --no-open-browser",
112
+ "dev:full": "concurrently \"npm run dev:api\" \"npm run dev\"",
113
+ "build": "npm run build:svelte && npm run build:cli && npm run postbuild:cli",
114
+ "build:svelte": "vite build",
115
+ "build:cli": "esbuild index.ts cli/**/*.ts --bundle --platform=node --target=node22 --format=esm --outdir=dist --external:express --external:commander --external:js-yaml --external:yaml --external:isomorphic-git --external:glob --external:open --external:ws --external:cors --external:multer --external:@octokit/rest --external:undici --external:exceljs --external:csv-parse",
116
+ "postbuild:cli": "cp cli-wrapper.mjs dist/lula2 && chmod +x dist/lula2",
117
+ "preview": "vite preview",
118
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && tsc --noEmit",
119
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
120
+ "format": "prettier --write 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
121
+ "format:check": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
122
+ "lint": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts' && eslint src cli",
123
+ "test": "npm run test:unit -- --run --coverage",
124
+ "test:integration": "vitest --config integration/vitest.config.integration.ts",
125
+ "test:unit": "vitest"
126
126
  }
127
- }
127
+ }
@@ -0,0 +1,24 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Lula Authors
3
+
4
+ /**
5
+ * Action that dispatches an event when a click occurs outside of the node it's applied to
6
+ */
7
+ export function clickOutside(node: HTMLElement, callback: () => void) {
8
+ const handleClick = (event: MouseEvent) => {
9
+ if (node && !node.contains(event.target as Node) && !event.defaultPrevented) {
10
+ callback();
11
+ }
12
+ };
13
+
14
+ document.addEventListener('click', handleClick, true);
15
+
16
+ return {
17
+ destroy() {
18
+ document.removeEventListener('click', handleClick, true);
19
+ },
20
+ update(newCallback: () => void) {
21
+ callback = newCallback;
22
+ }
23
+ };
24
+ }