lesli_shield 1.1.4 → 1.1.5

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.
@@ -0,0 +1,2 @@
1
+ /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-content:""}}}@layer theme{:root,:host{--color-amber-50:oklch(98.7% .022 95.277);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-950:oklch(27.9% .077 45.635);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-200:oklch(90.5% .093 164.15);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-700:oklch(50.8% .118 165.612);--color-sky-50:oklch(97.7% .013 236.62);--color-sky-200:oklch(90.1% .058 230.902);--color-sky-500:oklch(68.5% .169 237.323);--color-sky-600:oklch(58.8% .158 241.966);--color-sky-700:oklch(50% .134 242.749);--color-sky-800:oklch(44.3% .11 240.79);--color-sky-900:oklch(39.1% .09 240.876);--color-sky-950:oklch(29.3% .066 243.157);--color-rose-50:oklch(96.9% .015 12.422);--color-rose-200:oklch(89.2% .058 10.001);--color-rose-400:oklch(71.2% .194 13.428);--color-rose-500:oklch(64.5% .246 16.439);--color-rose-600:oklch(58.6% .253 17.585);--color-rose-700:oklch(51.4% .222 16.935);--color-slate-50:oklch(98.4% .003 247.858);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-600:oklch(44.6% .043 257.281);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-800:oklch(27.9% .041 260.031);--color-slate-900:oklch(20.8% .042 265.755);--color-slate-950:oklch(12.9% .042 264.695);--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-lg:32rem;--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--leading-tight:1.25;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1)}}@layer utilities{.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.inset-x-0{inset-inline:0}.bottom-0{bottom:0}.mx-auto{margin-inline:auto}.mt-1{margin-top:var(--spacing)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mr-1{margin-right:var(--spacing)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.h-5{height:calc(var(--spacing) * 5)}.h-auto{height:auto}.max-h-20{max-height:calc(var(--spacing) * 20)}.min-h-screen{min-height:100vh}.w-5{width:calc(var(--spacing) * 5)}.w-auto{width:auto}.w-full{width:100%}.max-w-\[220px\]{max-width:220px}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.flex-1{flex:1}.grid-cols-\[auto_1fr\]{grid-template-columns:auto 1fr}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-y-1{row-gap:var(--spacing)}.overflow-hidden{overflow:hidden}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-sky-200{border-color:var(--color-sky-200)}.bg-sky-50{background-color:var(--color-sky-50)}.bg-slate-50{background-color:var(--color-slate-50)}.bg-white{background-color:var(--color-white)}.bg-gradient-to-t{--tw-gradient-position:to top in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-slate-950\/70{--tw-gradient-from:#020618b3}@supports (color:color-mix(in lab, red, red)){.from-slate-950\/70{--tw-gradient-from:color-mix(in oklab, var(--color-slate-950) 70%, transparent)}}.from-slate-950\/70{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.via-slate-900\/10{--tw-gradient-via:#0f172b1a}@supports (color:color-mix(in lab, red, red)){.via-slate-900\/10{--tw-gradient-via:color-mix(in oklab, var(--color-slate-900) 10%, transparent)}}.via-slate-900\/10{--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.p-12{padding:calc(var(--spacing) * 12)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-10{padding-block:calc(var(--spacing) * 10)}.pt-6{padding-top:calc(var(--spacing) * 6)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.break-all{word-break:break-all}.text-sky-700{color:var(--color-sky-700)}.text-sky-800{color:var(--color-sky-800)}.text-sky-950{color:var(--color-sky-950)}.text-slate-600{color:var(--color-slate-600)}.text-slate-900{color:var(--color-slate-900)}.text-white{color:var(--color-white)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.hover\:text-sky-900:hover{color:var(--color-sky-900)}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-sky-500\/30:focus{--tw-ring-color:#00a5ef4d}@supports (color:color-mix(in lab, red, red)){.focus\:ring-sky-500\/30:focus{--tw-ring-color:color-mix(in oklab, var(--color-sky-500) 30%, transparent)}}.focus\:ring-offset-4:focus{--tw-ring-offset-width:4px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}@media (min-width:40rem){.sm\:px-10{padding-inline:calc(var(--spacing) * 10)}.sm\:pt-8{padding-top:calc(var(--spacing) * 8)}}@media (min-width:64rem){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:block{display:block}.lg\:grid{display:grid}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:px-16{padding-inline:calc(var(--spacing) * 16)}}}@layer components{.lesli-form-field{margin-bottom:calc(var(--spacing) * 4)}.lesli-form-label-wrapper{margin-bottom:calc(var(--spacing) * 2)}.lesli-form-label{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-slate-700);display:block}.lesli-form-field-body{min-width:0}.lesli-form-control{position:relative}.lesli-form-input,.lesli-form-select,.lesli-form-control textarea,.lesli-form-control trix-editor{border-radius:var(--radius-xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-slate-300);background-color:var(--color-white);width:100%;padding-inline:calc(var(--spacing) * 3.5);padding-block:calc(var(--spacing) * 2.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-slate-900);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-outline-style:none;outline-style:none;display:block}:is(.lesli-form-input,.lesli-form-select,.lesli-form-control textarea,.lesli-form-control trix-editor)::placeholder{color:var(--color-slate-400)}@media (hover:hover){:is(.lesli-form-input,.lesli-form-select,.lesli-form-control textarea,.lesli-form-control trix-editor):hover{border-color:var(--color-slate-400)}}:is(.lesli-form-input,.lesli-form-select,.lesli-form-control textarea,.lesli-form-control trix-editor):focus{border-color:var(--color-sky-500);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#00a5ef1a}@supports (color:color-mix(in lab, red, red)){:is(.lesli-form-input,.lesli-form-select,.lesli-form-control textarea,.lesli-form-control trix-editor):focus{--tw-ring-color:color-mix(in oklab, var(--color-sky-500) 10%, transparent)}}:is(.lesli-form-input,.lesli-form-select,.lesli-form-control textarea,.lesli-form-control trix-editor):disabled{cursor:not-allowed;background-color:var(--color-slate-100);color:var(--color-slate-500);opacity:.75}.lesli-form-input,.lesli-form-select{height:calc(var(--spacing) * 11)}.lesli-form-control textarea,.lesli-form-control trix-editor{min-height:calc(var(--spacing) * 32);resize:vertical}.lesli-form-select-wrapper{position:relative}.lesli-form-select{cursor:pointer;appearance:none;padding-right:calc(var(--spacing) * 10)}.lesli-form-select-wrapper:after{pointer-events:none;top:50%;right:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 2);width:calc(var(--spacing) * 2);--tw-translate-y:calc(var(--spacing) * -2);translate:var(--tw-translate-x) var(--tw-translate-y);border-right-style:var(--tw-border-style);border-right-width:2px;border-bottom-style:var(--tw-border-style);border-bottom-width:2px;border-color:var(--color-slate-400);--tw-content:"";content:var(--tw-content);position:absolute;rotate:45deg}.lesli-form-checkbox{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);cursor:pointer;border-color:var(--color-slate-300);accent-color:var(--color-sky-600);border-radius:.25rem}.lesli-form-checkbox:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#00a5ef4d}@supports (color:color-mix(in lab, red, red)){.lesli-form-checkbox:focus{--tw-ring-color:color-mix(in oklab, var(--color-sky-500) 30%, transparent)}}.lesli-form-checkbox:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.lesli-form-checkbox:disabled{cursor:not-allowed;opacity:.5}.lesli-form-checkbox-label{cursor:pointer;align-items:center;gap:calc(var(--spacing) * 2.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-slate-600);display:inline-flex}.lesli-form-control.has-icon .lesli-form-input,.lesli-form-control.has-icon .lesli-form-select{padding-left:calc(var(--spacing) * 11)}.lesli-form-icon{pointer-events:none;top:50%;left:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y);color:var(--color-slate-400);justify-content:center;align-items:center;display:inline-flex;position:absolute}.lesli-form-icon .material-symbols-outlined{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height));--tw-leading:1;line-height:1}.lesli-form-help{margin-top:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5);color:var(--color-slate-500)}.lesli-form-field.is-danger .lesli-form-input,.lesli-form-field.is-danger .lesli-form-select,.lesli-form-field.is-danger textarea,.lesli-form-field.is-danger trix-editor{border-color:var(--color-rose-400)}:is(.lesli-form-field.is-danger .lesli-form-input,.lesli-form-field.is-danger .lesli-form-select,.lesli-form-field.is-danger textarea,.lesli-form-field.is-danger trix-editor):focus{border-color:var(--color-rose-500);--tw-ring-color:#ff23571a}@supports (color:color-mix(in lab, red, red)){:is(.lesli-form-field.is-danger .lesli-form-input,.lesli-form-field.is-danger .lesli-form-select,.lesli-form-field.is-danger textarea,.lesli-form-field.is-danger trix-editor):focus{--tw-ring-color:color-mix(in oklab, var(--color-rose-500) 10%, transparent)}}.lesli-form-help.is-danger{color:var(--color-rose-700)}.lesli-form-help.is-warning{color:var(--color-amber-700)}.lesli-form-help.is-success{color:var(--color-emerald-700)}.lesli-form-help.is-info{color:var(--color-sky-700)}.lesli-form-button{height:calc(var(--spacing) * 11);cursor:pointer;justify-content:center;align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-xl);border-style:var(--tw-border-style);padding-inline:calc(var(--spacing) * 4);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-width:1px;border-color:#0000;display:inline-flex}.lesli-form-button:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-outline-style:none;outline-style:none}.lesli-form-button:disabled{pointer-events:none;cursor:not-allowed;opacity:.5}.lesli-form-button.is-primary,.lesli-form-button.is-info{background-color:var(--color-sky-600);color:var(--color-white)}@media (hover:hover){:is(.lesli-form-button.is-primary,.lesli-form-button.is-info):hover{background-color:var(--color-sky-700)}}:is(.lesli-form-button.is-primary,.lesli-form-button.is-info):focus{--tw-ring-color:#00a5ef33}@supports (color:color-mix(in lab, red, red)){:is(.lesli-form-button.is-primary,.lesli-form-button.is-info):focus{--tw-ring-color:color-mix(in oklab, var(--color-sky-500) 20%, transparent)}}.lesli-form-button.is-success{background-color:var(--color-emerald-600);color:var(--color-white)}@media (hover:hover){.lesli-form-button.is-success:hover{background-color:var(--color-emerald-700)}}.lesli-form-button.is-success:focus{--tw-ring-color:#00bb7f33}@supports (color:color-mix(in lab, red, red)){.lesli-form-button.is-success:focus{--tw-ring-color:color-mix(in oklab, var(--color-emerald-500) 20%, transparent)}}.lesli-form-button.is-warning{background-color:var(--color-amber-500);color:var(--color-amber-950)}@media (hover:hover){.lesli-form-button.is-warning:hover{background-color:var(--color-amber-600)}}.lesli-form-button.is-warning:focus{--tw-ring-color:#f99c0033}@supports (color:color-mix(in lab, red, red)){.lesli-form-button.is-warning:focus{--tw-ring-color:color-mix(in oklab, var(--color-amber-500) 20%, transparent)}}.lesli-form-button.is-danger{background-color:var(--color-rose-600);color:var(--color-white)}@media (hover:hover){.lesli-form-button.is-danger:hover{background-color:var(--color-rose-700)}}.lesli-form-button.is-danger:focus{--tw-ring-color:#ff235733}@supports (color:color-mix(in lab, red, red)){.lesli-form-button.is-danger:focus{--tw-ring-color:color-mix(in oklab, var(--color-rose-500) 20%, transparent)}}.lesli-form-button.is-fullwidth{width:100%}.lesli-form-button-icon{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);justify-content:center;align-items:center;display:inline-flex}.lesli-form-button-icon .material-symbols{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height));--tw-leading:1;line-height:1}.lesli-form-fieldset{margin-bottom:calc(var(--spacing) * 6);border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-slate-200);background-color:var(--color-white);padding:calc(var(--spacing) * 6);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.lesli-form-legend{margin-bottom:calc(var(--spacing) * 5);margin-left:calc(var(--spacing) * -1);padding-inline:var(--spacing);font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-slate-800)}.lesli-form-fieldset.is-info{border-color:var(--color-sky-200);background-color:#f0f9ff66}@supports (color:color-mix(in lab, red, red)){.lesli-form-fieldset.is-info{background-color:color-mix(in oklab, var(--color-sky-50) 40%, transparent)}}.lesli-form-fieldset.is-success{border-color:var(--color-emerald-200);background-color:#ecfdf566}@supports (color:color-mix(in lab, red, red)){.lesli-form-fieldset.is-success{background-color:color-mix(in oklab, var(--color-emerald-50) 40%, transparent)}}.lesli-form-fieldset.is-warning{border-color:var(--color-amber-200);background-color:#fffbeb80}@supports (color:color-mix(in lab, red, red)){.lesli-form-fieldset.is-warning{background-color:color-mix(in oklab, var(--color-amber-50) 50%, transparent)}}.lesli-form-fieldset.is-danger{border-color:var(--color-rose-200);background-color:#fff1f266}@supports (color:color-mix(in lab, red, red)){.lesli-form-fieldset.is-danger{background-color:color-mix(in oklab, var(--color-rose-50) 40%, transparent)}}@media (min-width:768px){.lesli-form-field.is-horizontal{align-items:flex-start;gap:calc(var(--spacing) * 5);grid-template-columns:minmax(8rem,1fr) minmax(0,3fr);display:grid}.lesli-form-field.is-horizontal>.lesli-form-label-wrapper{padding-top:calc(var(--spacing) * 2.5);text-align:right;margin-bottom:0}.lesli-form-field.is-horizontal>.lesli-form-field-body{grid-column-start:2}}.links{margin-top:calc(var(--spacing) * 5);border-radius:var(--radius-xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-slate-200);background-color:var(--color-white);overflow:hidden}.links>a{align-items:center;gap:calc(var(--spacing) * 3);width:100%;padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-slate-700);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:flex}@media (hover:hover){.links>a:hover{background-color:var(--color-slate-50);color:var(--color-sky-700)}}.links>a:focus{--tw-outline-style:none;outline-style:none}.links>a:focus-visible{background-color:var(--color-sky-50);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#00a5ef4d}@supports (color:color-mix(in lab, red, red)){.links>a:focus-visible{--tw-ring-color:color-mix(in oklab, var(--color-sky-500) 30%, transparent)}}.links>a:focus-visible{--tw-ring-inset:inset}.links>a:not(:last-child){border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-slate-200)}.links .icon{height:calc(var(--spacing) * 7);width:calc(var(--spacing) * 7);flex-shrink:0;justify-content:center;align-items:center;margin-right:0;display:inline-flex}.links .icon svg{max-height:calc(var(--spacing) * 6);max-width:calc(var(--spacing) * 6);display:block}.links>a>span:not(.icon){font-family:Domine,OpenSans,sans-serif}.lesli-auth-visual{background-color:#0000;background-image:linear-gradient(#316aff66,#316aff66),url(/lesli_shield/login-background.jpg);background-position:50%;background-repeat:repeat,repeat;background-size:cover;background-attachment:scroll,scroll;background-origin:padding-box,padding-box;background-clip:border-box,border-box}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"<length-percentage>";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"<length-percentage>";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"<length-percentage>";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-content{syntax:"*";inherits:false;initial-value:""}
@@ -56,10 +56,44 @@ module LesliShield
56
56
 
57
57
  def create_account
58
58
 
59
- # check if instance is for multi-account
60
- allow_multiaccount = Lesli.config.security.dig(:allow_multiaccount)
59
+ add_account() if resource.account.blank?
60
+ add_role() if resource.user_roles.empty?
61
61
 
62
- if allow_multiaccount === true
62
+ # Synchronize roles for the very first time
63
+ resource.roles.each do |role|
64
+ LesliShield::RolePrivilegeService.new(nil).synchronize(role)
65
+ end
66
+
67
+ # update user :)
68
+ resource.save
69
+
70
+ # # initialize user dependencies
71
+ # def after_account_assignation
72
+ # return unless self.account
73
+
74
+ # #Courier::One::Firebase::User.sync_user(self)
75
+ # # Lesli::Courier.new(:lesli_calendar).from(:calendar_service, self).create({
76
+ # # name: "Personal Calendar",
77
+ # # default: true
78
+ # # })
79
+ # end
80
+
81
+ end
82
+
83
+ private
84
+
85
+ # check if instance is for multi-account
86
+ ALLOW_MULTI_ACCOUNT = Lesli.config.security.dig(:allow_multiaccount)
87
+
88
+ # This is necessary when user is creating a brand new account
89
+ # and the platform is configured to create new Lesli accounts for every user
90
+ # The platform use the same account when the platform is not saas but
91
+ # proprietary instance (only one company is using the platform)
92
+ # Another scenario where it is not needed to create a new account for the
93
+ # user is when the user is confirming the account through an invitation
94
+ def add_account
95
+
96
+ if ALLOW_MULTI_ACCOUNT === true
63
97
  # create new account for the new user only if multi-account is allowed
64
98
  account = Lesli::Account.create!({
65
99
  user: resource, # set user as owner of his just created account
@@ -74,42 +108,21 @@ module LesliShield
74
108
 
75
109
  # add user to his own account
76
110
  resource.account = account
77
-
78
- if allow_multiaccount == true
111
+ end
112
+
113
+ def add_role
114
+ if ALLOW_MULTI_ACCOUNT == true
79
115
  # add owner role to user only if multi-account is allowed
80
116
  resource.user_roles.create({ role: account.roles.find_by(name: "owner") })
81
117
  else
82
118
  # Assigning default role if defined in account settings
83
119
  # Otherwise, the default role is "limited"
84
- #default_role_id = account.settings.find_by(:name => "default_role_id")&.value
85
- default_role_id = Lesli.config.shield.dig(:default_role)
86
-
87
- if default_role_id.present?
88
- resource.roles.create({ role: account.roles.find_by(:id => default_role_id)})
89
- else
90
- resource.roles.create({ role: account.roles.find_by(name: "limited") })
91
- end
92
- end
93
-
94
- # Synchronize roles for the very first time
95
- resource.roles.each do |role|
96
- LesliShield::RolePrivilegeService.new(nil).synchronize(role)
97
- end
120
+ default_role_name = Lesli.config.shield.dig(:default_role) || "guest"
98
121
 
99
- # update user :)
100
- resource.save
101
-
102
- # # initialize user dependencies
103
- # def after_account_assignation
104
- # return unless self.account
105
-
106
- # #Courier::One::Firebase::User.sync_user(self)
107
- # # Lesli::Courier.new(:lesli_calendar).from(:calendar_service, self).create({
108
- # # name: "Personal Calendar",
109
- # # default: true
110
- # # })
111
- # end
122
+ default_role = resource.account.roles.find_by(name: default_role_name)
112
123
 
124
+ resource.user_roles.create({ role: default_role })
125
+ end
113
126
  end
114
127
  end
115
128
  end
@@ -44,59 +44,63 @@ title = I18n.t("overrides.lesli_shield.devise/sessions.view_title",
44
44
  default: :"lesli_shield.devise/sessions.view_title")
45
45
  %>
46
46
 
47
- <%= render("devise/shared/application-devise", title:title) do %>
47
+ <%= render("devise/shared/application-devise", title: title) do %>
48
+
49
+ <% authentication_error = flash[:alert].presence || flash[:error].presence %>
48
50
 
49
51
  <%= form_for(
50
- resource,
51
- as: resource_name,
52
+ resource,
53
+ as: resource_name,
52
54
  url: user_session_path,
53
55
  builder: LesliView::Forms::Builder,
56
+ html: { class: "lesli-login-form" },
54
57
  data: { turbo: false }
55
- ) do |form| %>
58
+ ) do |form| %>
56
59
 
57
60
  <% if Lesli.config.demo %>
58
61
  <%= render("devise/shared/demo") %>
59
62
  <% end %>
60
63
 
61
- <%= form.field_control_builder(
62
- label_html:form.label(:email, "E-mail address"),
63
- control_html:form.email_field(:email, autofocus: true, autocomplete: "email", placeholder: Lesli.config.company[:email]),
64
- category: flash.first&.first,
65
- icon: "person"
64
+ <%= form.field_control_email(
65
+ :email,
66
+ label: I18n.t("lesli_shield.devise/sessions.label_email", default: "Email address"),
67
+ category: authentication_error.present? ? :danger : nil,
68
+ icon: "person",
69
+ autofocus: true,
70
+ autocomplete: "email",
71
+ required: true,
72
+ placeholder: Lesli.config.company[:email]
66
73
  ) %>
67
74
 
68
- <%= form.field_control_builder(
69
- label_html:form.label(:password),
70
- control_html:form.password_field(:password, autocomplete: "current-password", placeholder: "* * * * * *"),
71
- message_text: flash.first&.second,
72
- category: flash.first&.first,
73
- icon: "key"
75
+ <%= form.field_control_password(
76
+ :password,
77
+ label: I18n.t("lesli_shield.devise/sessions.label_password", default: "Password"),
78
+ message: authentication_error,
79
+ category: authentication_error.present? ? :danger : nil,
80
+ icon: "key",
81
+ autocomplete: "current-password",
82
+ required: true,
83
+ placeholder: "••••••••"
74
84
  ) %>
75
85
 
76
86
  <% if params[:r].presence %>
77
- <%= tag(:input, :type => "hidden", :name => "user[redirect]", :value => params[:r]) %>
87
+ <%= hidden_field_tag "user[redirect]", params[:r] %>
78
88
  <% end %>
79
89
 
80
- <div class="field mb-4 mt-5">
81
- <div class="control is-fullwidth">
82
- <button
83
- type="submit"
84
- class="button is-primary is-fullwidth mb-2">
85
- <%= I18n.t("lesli_shield.devise/sessions.button_log_in") %>
86
- </button>
87
- </div>
88
- </div>
90
+ <%= form.field_control_button(
91
+ I18n.t("lesli_shield.devise/sessions.button_log_in"),
92
+ { class: "is-fullwidth" },
93
+ icon: "login"
94
+ ) %>
89
95
 
90
96
  <% if Lesli.config.security.dig(:allow_registration) %>
91
- <p class="account has-text-centered">
97
+ <p class="mt-1 text-center text-sm text-slate-600">
92
98
  <%= I18n.t("lesli_shield.devise/sessions.view_still_not_registered") %>
93
- <%= link_to(new_user_registration_path, :class => "has-text-primary") do %>
94
- <u><b><%= I18n.t("lesli_shield.devise/sessions.button_create_account") %></b></u>
95
- <% end %>
96
- </p>
99
+ <%= link_to I18n.t("lesli_shield.devise/sessions.button_create_account"),
100
+ new_user_registration_path %>
101
+ </p>
97
102
  <% end %>
98
-
99
103
  <% end %>
100
104
 
101
- <%= render("devise/shared/links", password_reset:true, magic_link:false, otp:false) %>
105
+ <%= render("devise/shared/links", password_reset:true, magic_link:true, otp:true, login:true) %>
102
106
  <% end %>
@@ -30,46 +30,48 @@ Building a better future, one line of code at a time.
30
30
  // ·
31
31
  %>
32
32
 
33
- <% title = defined?(title) ? title : "" %>
34
- <% back_to_login = defined?(back_to_login) ? back_to_login : false %>
33
+ <% title = local_assigns.fetch(:title, "") %>
34
+ <% back_to_login = local_assigns.fetch(:back_to_login, false) %>
35
35
 
36
- <main id="lesli-application" class="standard-auth-view">
37
- <section class="columns is-gapless">
38
- <div class="column login-form">
39
- <div class="hero is-fullheight">
40
- <% if back_to_login %>
41
- <div class="hero-head pt-6 pl-6">
42
- <%= link_to new_user_session_path, :class => "has-text-primary" do %>
43
- <span class="icon-text">
44
- <span class="icon ri-lg">
45
- <i class="ri-arrow-left-s-line"></i>
46
- </span>
47
- <span>Back to Login</span>
48
- </span>
49
- <% end %>
50
- </div>
36
+ <main id="lesli-application" class="min-h-screen bg-slate-50 lg:grid lg:grid-cols-3">
37
+ <section class="flex min-h-screen flex-col bg-white">
38
+ <% if back_to_login %>
39
+ <div class="px-6 pt-6 sm:px-10 sm:pt-8">
40
+ <%= link_to new_user_session_path,
41
+ class: "inline-flex items-center gap-2 rounded-lg text-sm font-medium text-sky-700 transition hover:text-sky-900 focus:outline-none focus:ring-2 focus:ring-sky-500/30 focus:ring-offset-4" do %>
42
+ <svg class="h-5 w-5" viewBox="0 0 20 20" fill="none" aria-hidden="true">
43
+ <path d="m12.5 15-5-5 5-5" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"/>
44
+ </svg>
45
+ <span><%= I18n.t("actions.back_to_login", default: "Back to login") %></span>
51
46
  <% end %>
52
- <div class="hero-body is-flex-direction-column is-justify-content-center">
53
-
54
- <%# Logo container %>
55
- <div class="logo has-text-centered mb-6">
56
- <%= image_tag(
57
- "lesli_assets/brand/app-auth.svg",
58
- :class => "#{lesli_instance_code}-logo mb-2",
59
- :alt => "Main logo")
60
- %>
61
- <h1 class="is-title is-size-3 has-text-primary">
62
- <%= title %>
63
- </h1>
64
- </div>
47
+ </div>
48
+ <% end %>
65
49
 
66
- <%= yield %>
50
+ <div class="flex flex-1 items-center justify-center px-6 py-10 sm:px-10 lg:px-16">
51
+ <div class="w-full max-w-md">
52
+ <div class="mb-8 text-center">
53
+ <%= image_tag(
54
+ "lesli_assets/brand/app-auth.svg",
55
+ class: "#{lesli_instance_code}-logo mx-auto mb-6 h-auto max-h-20 w-auto max-w-[220px]",
56
+ alt: I18n.t("lesli_shield.authentication.logo_alt", default: "Application logo")
57
+ ) %>
67
58
 
59
+ <% if title.present? %>
60
+ <h1 class="text-3xl font-semibold tracking-tight text-slate-900"><%= title %></h1>
61
+ <% end %>
68
62
  </div>
69
- <%# footer %>
70
- <%#= render partial: "devise/shared/footer" %>
63
+
64
+ <%= yield %>
71
65
  </div>
72
66
  </div>
73
- <div class="column background is-hidden-touch"></div>
74
67
  </section>
68
+
69
+ <aside class="lesli-auth-visual relative hidden min-h-screen overflow-hidden lg:col-span-2 lg:block" aria-hidden="true">
70
+ <div class="absolute inset-0 bg-gradient-to-t from-slate-950/70 via-slate-900/10 to-transparent"></div>
71
+ <div class="absolute inset-x-0 bottom-0 p-12 text-white">
72
+ <p class="max-w-lg text-3xl font-semibold leading-tight">
73
+ <%= I18n.t("lesli_shield.authentication.tagline", default: "A better way to manage your work.") %>
74
+ </p>
75
+ </div>
76
+ </aside>
75
77
  </main>
@@ -1,6 +1,10 @@
1
1
 
2
- <fieldset class="shield-form-demo mb-4 px-4 pb-2">
3
- <legend>demo account</legend>
4
- <p><small><b>Username: </b><%= Lesli.config.company[:email] %></small></label>
5
- <p><small><b>Password: </b><%= Lesli.config.security[:password] %></small></p>
2
+ <fieldset class="mb-8 rounded-xl border border-sky-200 bg-sky-50 px-4 pb-4 text-sm text-sky-950">
3
+ <legend class="px-2 font-semibold text-sky-800">Demo account</legend>
4
+ <dl class="mt-1 grid grid-cols-[auto_1fr] gap-x-3 gap-y-1">
5
+ <dt class="font-medium">Username:</dt>
6
+ <dd class="break-all"><%= Lesli.config.company[:email] %></dd>
7
+ <dt class="font-medium">Password:</dt>
8
+ <dd><%= Lesli.config.security[:password] %></dd>
9
+ </dl>
6
10
  </fieldset>
@@ -1,4 +1,4 @@
1
1
  module LesliShield
2
- VERSION = "1.1.4"
3
- BUILD = "1780272810"
2
+ VERSION = "1.1.5"
3
+ BUILD = "1789353654"
4
4
  end
data/readme.md CHANGED
@@ -1,14 +1,15 @@
1
1
  <div align="center">
2
- <img width="100" alt="LesliShield logo" src="./app/assets/images/lesli_shield/shield-logo.svg" />
3
- <h3 align="center">Users & Security Management for the Lesli Framework.</h3>
2
+ <h1 align="center">
3
+ <img width="100" alt="LesliShield" src="./app/assets/images/lesli_shield/shield-logo.svg" />
4
+ </h1>
5
+ <h3 align="center">Authentication and authorization for the Lesli Framework.</h3>
4
6
  </div>
5
7
 
6
8
  <br />
7
- <hr/>
8
9
 
9
10
  <div align="center">
10
- <a target="_blank" href="https://github.com/LesliTech/LesliShield/actions">
11
- <img alt="Tests passing" src="https://img.shields.io/badge/Tests-passing-green?style=for-the-badge&logo=github">
11
+ <a target="_blank" href="https://github.com/LesliTech/LesliShield/actions/workflows/lesli-ci-tests.yaml">
12
+ <img alt="LesliShield test status" src="https://img.shields.io/github/actions/workflow/status/LesliTech/LesliShield/lesli-ci-tests.yaml?branch=master&style=for-the-badge&logo=github&label=tests">
12
13
  </a>
13
14
  <a target="_blank" href="https://rubygems.org/gems/lesli_shield">
14
15
  <img alt="Gem Version" src="https://img.shields.io/gem/v/lesli_shield?style=for-the-badge&logo=ruby">
@@ -16,84 +17,148 @@
16
17
  <a target="_blank" href="https://codecov.io/github/LesliTech/LesliShield">
17
18
  <img alt="Codecov" src="https://img.shields.io/codecov/c/github/LesliTech/LesliShield?style=for-the-badge&logo=codecov">
18
19
  </a>
20
+ <a target="_blank" href="https://sonarcloud.io/project/overview?id=LesliTech_LesliShield">
21
+ <img alt="Sonar Quality Gate" src="https://img.shields.io/sonar/quality_gate/LesliTech_LesliShield?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge&logo=sonarqubecloud&label=Quality">
22
+ </a>
19
23
  </div>
20
24
 
21
- <hr/>
22
25
  <br />
23
26
 
24
27
  <div align="center">
25
28
  <img
26
29
  style="width:100%;max-width:800px;border-radius:6px;"
27
- alt="Lesli screenshot" src="./docs/images/screenshot.png" />
30
+ alt="LesliShield security administration"
31
+ src="./docs/images/screenshot.png" />
28
32
  </div>
29
33
 
34
+ ---
35
+
36
+ <br />
37
+
38
+ ## Introduction
39
+
40
+ LesliShield is the official authentication and authorization engine for the [Lesli Framework](https://github.com/LesliTech/Lesli).
41
+
42
+ It centralizes account security, role-based access control, and user onboarding for Lesli applications.
43
+
30
44
  <br />
31
- <hr/>
45
+
46
+ ## Features
47
+
48
+ - Authentication and session management
49
+ - Role-based access control
50
+ - Permission and privilege administration
51
+ - Account-scoped security policies
52
+ - User invitations and onboarding
53
+
32
54
  <br />
33
55
 
34
- ### Installation
56
+ ## Try LesliShield
35
57
 
36
- ```ruby
58
+ - [Try the online demo](https://demo.lesli.dev/)
59
+ - [Run the Docker demo](https://github.com/LesliTech/lesli-docker-demo)
37
60
 
38
- # Add LesliShield engine gem
61
+ <br />
62
+
63
+ ## Quick Start
64
+
65
+ ### Requirements
66
+
67
+ - A Rails application with [Lesli](https://rubygems.org/gems/lesli)
68
+ - SQLite by default, or PostgreSQL when preferred by the host application
69
+
70
+ ### Install LesliShield
71
+
72
+ Add the engine to the host Rails application and prepare its database:
73
+
74
+ ```shell
39
75
  bundle add lesli_shield
76
+ bin/rails db:prepare
77
+ ```
78
+
79
+ ### Mount the engine
40
80
 
81
+ Applications using Lesli's standard router mount LesliShield automatically at `/shield`:
41
82
 
42
- # Setup & initialize the database
43
- rake lesli:db:setup
83
+ ```ruby
84
+ # config/routes.rb
85
+ Rails.application.routes.draw do
86
+ Lesli::Router.mount(self)
87
+ end
88
+ ```
44
89
 
90
+ If the application does not use the standard Lesli router, mount the engine directly:
45
91
 
46
- # Load LesliShield engine
92
+ ```ruby
93
+ # config/routes.rb
47
94
  Rails.application.routes.draw do
48
95
  mount LesliShield::Engine => "/shield"
49
96
  end
50
97
  ```
51
98
 
52
- <br />
53
- <hr />
99
+ Start Rails and visit [http://127.0.0.1:3000/shield](http://127.0.0.1:3000/shield):
100
+
101
+ ```shell
102
+ bin/rails server
103
+ ```
104
+
54
105
  <br />
55
106
 
56
- ### Development
107
+ ## Development
108
+
109
+ Clone LesliShield into the host application's `engines` directory:
110
+
111
+ ```shell
112
+ cd RailsApp
113
+ mkdir -p engines
114
+ git clone https://github.com/LesliTech/LesliShield.git engines/LesliShield
115
+ ```
116
+
117
+ Reference the local engine from the host application's `Gemfile`:
57
118
 
58
119
  ```ruby
59
- # clone the lesli repo inside your engine folder: RailsApp/engines
60
- git clone https://github.com/LesliTech/LesliShield.git
120
+ gem "lesli_shield", path: "engines/LesliShield"
121
+ ```
61
122
 
62
- # Load LesliShield as a Gem
63
- gem "lesli", path: "engines/LesliShield"
123
+ Install dependencies, prepare the host database, and start Rails:
64
124
 
65
- # Install the necessary Gems to run LesliShield
125
+ ```shell
66
126
  bundle install
127
+ bin/rails db:prepare
128
+ bin/rails server
129
+ ```
67
130
 
68
- # Setup & initialize the database
69
- rake lesli:db:setup
131
+ ### Tests
70
132
 
133
+ From a complete Lesli development workspace, run the engine test suite from the LesliShield directory:
134
+
135
+ ```shell
136
+ cd engines/LesliShield
137
+ bin/rails test
71
138
  ```
72
139
 
73
140
  <br />
74
- <hr />
75
- <br />
76
-
77
- ### Demo
78
141
 
79
- * [online demo](https://demo.lesli.dev/)
80
- * [docker demo](https://github.com/LesliTech/lesli-docker-demo)
142
+ ## Documentation
81
143
 
144
+ - [Lesli website](https://www.lesli.dev/)
145
+ - [Documentation](https://www.lesli.dev/engines/shield)
146
+ - [Release notes](https://github.com/LesliTech/LesliShield/releases)
147
+ - [Issue tracker](https://github.com/LesliTech/LesliShield/issues)
148
+ - [Source code](https://github.com/LesliTech/LesliShield)
82
149
 
83
- ### Documentation
84
- * [website](https://www.lesli.dev/)
85
- * [documentation](https://www.lesli.dev/engines/shield)
150
+ <br />
86
151
 
152
+ ## Community
87
153
 
88
- ### Connect with Lesli
154
+ - [X: @LesliTech](https://x.com/LesliTech)
155
+ - [hello@lesli.tech](mailto:hello@lesli.tech)
156
+ - [https://www.lesli.tech](https://www.lesli.tech)
89
157
 
90
- * [@LesliTech](https://x.com/LesliTech)
91
- * [hello@lesli.tech](hello@lesli.tech)
92
- * [https://www.lesli.tech](https://www.lesli.tech)
158
+ <br />
93
159
 
160
+ ## License
94
161
 
95
- ### License
96
- -------
97
162
  Copyright (c) 2026, Lesli Technologies, S. A.
98
163
 
99
164
  This program is free software: you can redistribute it and/or modify
@@ -107,18 +172,16 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
107
172
  GNU General Public License for more details.
108
173
 
109
174
  You should have received a copy of the GNU General Public License
110
- along with this program. If not, see http://www.gnu.org/licenses/.
175
+ along with this program. If not, see [https://www.gnu.org/licenses/](https://www.gnu.org/licenses/).
111
176
 
112
- <hr />
113
- <br />
114
- <br />
177
+ The complete license text is available in the [license file](./license).
115
178
 
116
- <div align="center" class="has-text-centered">
117
- <img width="200" alt="Lesli logo" src="https://cdn.lesli.tech/lesli/brand/app-logo.svg" />
118
- <h3 align="center" class="mt-0">
119
- The Open-Source SaaS Development Framework for Ruby on Rails.
120
- </h3>
121
- </div>
179
+ ---
122
180
 
123
181
  <br />
124
182
  <br />
183
+
184
+ <div align="center">
185
+ <img width="80" alt="Lesli icon" src="https://cdn.lesli.tech/lesli/brand/app-icon.svg" />
186
+ <h3 align="center">The Open-Source SaaS Development Framework for Ruby on Rails.</h3>
187
+ </div>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lesli_shield
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 1.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - The Lesli Development Team
@@ -43,11 +43,13 @@ files:
43
43
  - app/assets/javascripts/lesli_shield/registrations.js
44
44
  - app/assets/javascripts/lesli_shield/sessions.js
45
45
  - app/assets/stylesheets/lesli_shield/application.css
46
+ - app/assets/stylesheets/lesli_shield/application.tailwind.css
46
47
  - app/assets/stylesheets/lesli_shield/confirmations.css
47
48
  - app/assets/stylesheets/lesli_shield/devise/oauth.css
48
49
  - app/assets/stylesheets/lesli_shield/passwords.css
49
50
  - app/assets/stylesheets/lesli_shield/registrations.css
50
51
  - app/assets/stylesheets/lesli_shield/sessions.css
52
+ - app/assets/stylesheets/lesli_shield/sessions.tailwind.css
51
53
  - app/assets/stylesheets/lesli_shield/users.css
52
54
  - app/controllers/lesli_shield/application_controller.rb
53
55
  - app/controllers/lesli_shield/dashboards_controller.rb