mnfst 0.5.53 → 0.5.55

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.
@@ -161,6 +161,11 @@ window.ManifestComponentsProcessor = {
161
161
  return;
162
162
  }
163
163
  if (element.hasAttribute('data-pre-rendered') || element.hasAttribute('data-processed')) {
164
+ // Pre-rendered components skip re-fetching, but hydrate-marked content
165
+ // still needs Alpine initialization (x-data, @click, :class, x-theme etc.).
166
+ if (element.hasAttribute('data-pre-rendered') && window.Alpine && typeof window.Alpine.initTree === 'function') {
167
+ try { window.Alpine.initTree(element); } catch (e) { /* graceful */ }
168
+ }
164
169
  return;
165
170
  }
166
171
  const content = await loader.loadComponent(name);
package/lib/manifest.css CHANGED
@@ -690,6 +690,404 @@
690
690
  }
691
691
  }
692
692
 
693
+ /* Manifest Color Picker */
694
+
695
+ :root {
696
+ --icon-alpha-pattern: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 2 2'%3E%3Crect width='1' height='1' x='0' y='0' fill='%23808080' opacity='0.15'/%3E%3Crect width='1' height='1' x='1' y='1' fill='%23808080' opacity='0.15'/%3E%3C/svg%3E");
697
+ }
698
+
699
+ @layer utilities {
700
+
701
+ /* Swatch trigger button */
702
+ :where(.colorpicker-swatch):not(.unstyle) {
703
+ position: relative;
704
+ width: var(--spacing-field-height, 2.25rem);
705
+ height: var(--spacing-field-height, 2.25rem);
706
+ min-width: var(--spacing-field-height, 2.25rem);
707
+ max-width: var(--spacing-field-height, 2.25rem);
708
+ padding: 0;
709
+ background: var(--swatch-color, #000000);
710
+ border-width: 1px;
711
+ border-style: solid;
712
+ border-color: oklch(from var(--swatch-color, #000000) calc(l + (0.5 - l) * 0.35) c calc(h + 0));
713
+ border-radius: var(--radius, 0.5rem);
714
+ cursor: pointer;
715
+ overflow: hidden;
716
+ transition: var(--transition);
717
+
718
+ &:hover,
719
+ &:active,
720
+ &:focus,
721
+ &:focus-visible {
722
+ border-color: oklch(from var(--swatch-color, #000000) calc(l + (0.5 - l) * 0.35) c calc(h + 0) / 0.5)
723
+ }
724
+
725
+ /* Alpha pattern background */
726
+ &::before {
727
+ content: "";
728
+ position: absolute;
729
+ top: 0;
730
+ left: 0;
731
+ width: 100%;
732
+ height: 100%;
733
+ z-index: -1;
734
+ background-image: var(--icon-alpha-pattern);
735
+ background-size: 50%;
736
+ background-position: top left
737
+ }
738
+ }
739
+
740
+ /* Dropdown menu — unwrapped from :where() to beat base menu[popover] specificity */
741
+ [x-colorpicker]:not(.unstyle) {
742
+
743
+ /* Resetting dropdown styles */
744
+ & :where(hr) {
745
+ margin-top: 2px;
746
+ margin-bottom: 2px
747
+ }
748
+
749
+ /* & :where(li, a, button, label) {
750
+ width: fit-content;
751
+ min-width: var(--spacing-field-height, 2.25rem)
752
+ } */
753
+
754
+ /* Canvas wrapper */
755
+ & .canvas-wrapper {
756
+ position: relative;
757
+ width: 100%;
758
+ aspect-ratio: 1;
759
+ overflow: hidden;
760
+ cursor: crosshair;
761
+ touch-action: none
762
+ }
763
+
764
+ /* Canvas (SV plane) */
765
+ & canvas {
766
+ display: block;
767
+ width: 100%;
768
+ height: 100%
769
+ }
770
+
771
+ /* Reticle indicator */
772
+ & .color-reticle {
773
+ position: absolute;
774
+ width: 0.75rem;
775
+ height: 0.75rem;
776
+ z-index: 1;
777
+ border: 2px solid #ffffff;
778
+ border-radius: 50%;
779
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px rgba(0, 0, 0, 0.2);
780
+ transform: translate(-50%, -50%);
781
+ pointer-events: none;
782
+ }
783
+
784
+ /* Optional color controls container */
785
+ & .color-controls {
786
+ display: flex;
787
+ flex-direction: column;
788
+ gap: 0.75rem;
789
+ padding: 0.75rem
790
+ }
791
+
792
+ /* Shared slider styles */
793
+ & input[type=range].hue,
794
+ & input[type=range].alpha {
795
+ width: 100%;
796
+ height: 0.75rem;
797
+ padding: 0;
798
+ border-radius: 1rem;
799
+ appearance: none;
800
+ -webkit-appearance: none;
801
+ cursor: pointer;
802
+ outline: none;
803
+ border: none;
804
+ background: transparent;
805
+
806
+ /* Webkit thumb */
807
+ &::-webkit-slider-thumb {
808
+ -webkit-appearance: none;
809
+ width: 1rem;
810
+ height: 1rem;
811
+ border-radius: 50%;
812
+ background: #ffffff;
813
+ border: 2px solid #ffffff;
814
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15), 0 1px 3px rgba(0, 0, 0, 0.25);
815
+ cursor: grab;
816
+
817
+ &:active {
818
+ cursor: grabbing;
819
+ }
820
+ }
821
+
822
+ /* Firefox thumb */
823
+ &::-moz-range-thumb {
824
+ width: 1rem;
825
+ height: 1rem;
826
+ border-radius: 50%;
827
+ background: #ffffff;
828
+ border: 2px solid #ffffff;
829
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15), 0 1px 3px rgba(0, 0, 0, 0.25);
830
+ cursor: grab;
831
+
832
+ &:active {
833
+ cursor: grabbing;
834
+ }
835
+ }
836
+
837
+ /* Webkit track */
838
+ &::-webkit-slider-runnable-track {
839
+ height: 0.75rem;
840
+ border-radius: 1rem
841
+ }
842
+
843
+ /* Firefox track */
844
+ &::-moz-range-track {
845
+ height: 0.75rem;
846
+ border-radius: 1rem
847
+ }
848
+ }
849
+
850
+ /* Hue slider - static rainbow gradient */
851
+ & input[type=range].hue {
852
+ &::-webkit-slider-runnable-track {
853
+ background: linear-gradient(to right,
854
+ hsl(0 100% 50%), hsl(60 100% 50%), hsl(120 100% 50%),
855
+ hsl(180 100% 50%), hsl(240 100% 50%), hsl(300 100% 50%),
856
+ hsl(360 100% 50%))
857
+ }
858
+
859
+ &::-moz-range-track {
860
+ background: linear-gradient(to right,
861
+ hsl(0 100% 50%), hsl(60 100% 50%), hsl(120 100% 50%),
862
+ hsl(180 100% 50%), hsl(240 100% 50%), hsl(300 100% 50%),
863
+ hsl(360 100% 50%))
864
+ }
865
+ }
866
+
867
+ /* Alpha slider - checkerboard + color gradient (set via JS --cp-color) */
868
+ & input[type=range].alpha {
869
+ --cp-color: rgb(0, 0, 0);
870
+ position: relative;
871
+
872
+ &::-webkit-slider-runnable-track {
873
+ background:
874
+ linear-gradient(to right, transparent, var(--cp-color)),
875
+ repeating-conic-gradient(#e0e0e0 0% 25%, #ffffff 0% 50%) 0 0 / 0.5rem 0.5rem
876
+ }
877
+
878
+ &::-moz-range-track {
879
+ background:
880
+ linear-gradient(to right, transparent, var(--cp-color)),
881
+ repeating-conic-gradient(#e0e0e0 0% 25%, #ffffff 0% 50%) 0 0 / 0.5rem 0.5rem
882
+ }
883
+ }
884
+
885
+ /* Color space selector */
886
+ & select.color-format {
887
+ width: 8ch;
888
+ padding-inline-start: 0;
889
+ padding-inline-end: 0;
890
+ font-size: 0.6875rem;
891
+
892
+ &::picker-icon {
893
+ display: none
894
+ }
895
+ }
896
+
897
+ /* Color value input */
898
+ & input.color-value {
899
+ flex: 1;
900
+ padding-inline-end: 0;
901
+ font-size: 50%
902
+ }
903
+
904
+ /* Alpha value input — hide spin buttons */
905
+ & input.alpha-value {
906
+ width: 6ch;
907
+ padding-inline-end: 0;
908
+ font-size: 50%;
909
+ -moz-appearance: textfield;
910
+
911
+ &::-webkit-inner-spin-button,
912
+ &::-webkit-outer-spin-button {
913
+ -webkit-appearance: none;
914
+ margin: 0
915
+ }
916
+ }
917
+
918
+ /* Gradient layers container */
919
+ & .gradient-layers {
920
+ display: flex;
921
+ flex-direction: column;
922
+ gap: 0.5rem;
923
+ }
924
+
925
+ /* Floating menus (non-popover, manually positioned) */
926
+ & .layer-angle-menu,
927
+ & .stop-floating-menu {
928
+ display: none;
929
+ flex-direction: column;
930
+ min-width: 140px;
931
+ margin: 0;
932
+ padding: 0.25rem;
933
+ list-style: none;
934
+ background: var(--color-popover-surface, oklch(100% 0 0));
935
+ border-radius: var(--radius, 0.5rem);
936
+ box-shadow: rgba(15, 15, 15, 0.05) 0px 0px 0px 1px, rgba(15, 15, 15, 0.1) 0px 3px 6px, rgba(15, 15, 15, 0.2) 0px 9px 24px;
937
+ z-index: 100;
938
+
939
+ & :where(li) {
940
+ display: flex;
941
+ align-items: center;
942
+ padding: 0.25rem 0.5rem;
943
+ font-size: 0.8125rem;
944
+ color: var(--color-content-stark, oklch(16.6% 0.026 267));
945
+ border-radius: 6px;
946
+ cursor: pointer;
947
+ user-select: none;
948
+
949
+ &:hover {
950
+ background-color: var(--color-field-surface, oklch(91.79% 0.0029 264.26));
951
+ }
952
+
953
+ &.negative:hover {
954
+ color: var(--color-negative-content, #ef4444);
955
+ }
956
+ }
957
+
958
+ & :where(hr) {
959
+ margin: 0.25rem 0;
960
+ border: none;
961
+ border-top: 1px solid var(--color-line, color-mix(in oklch, oklch(16.6% 0.026 267) 11%, transparent));
962
+ }
963
+
964
+ & :where(small) {
965
+ display: block;
966
+ padding: 0.25rem 0.5rem 0.125rem;
967
+ font-size: 0.6875rem;
968
+ color: var(--color-content-subtle, oklch(52.38% 0.017 264.26));
969
+ }
970
+ }
971
+
972
+ & .layer-angle-menu.show,
973
+ & .stop-floating-menu {
974
+ display: flex;
975
+ }
976
+
977
+ /* Stop-floating-menu always shows when present (added to DOM) */
978
+ & .stop-floating-menu {
979
+ display: flex;
980
+ }
981
+
982
+ /* Angle input label */
983
+ & .layer-angle {
984
+ display: inline-flex;
985
+ align-items: center;
986
+ cursor: ew-resize;
987
+
988
+ & input[type=number] {
989
+ width: 4ch;
990
+ padding: 0 0.125rem;
991
+ font-size: 0.625rem;
992
+ font-family: ui-monospace, monospace;
993
+ text-align: right;
994
+ background: transparent;
995
+ border: none;
996
+ color: var(--color-content-stark, oklch(16.6% 0.026 267));
997
+ outline: none;
998
+ cursor: ew-resize;
999
+ -moz-appearance: textfield !important;
1000
+ appearance: textfield !important;
1001
+
1002
+ &:focus {
1003
+ cursor: text;
1004
+ background-color: var(--color-field-surface, oklch(91.79% 0.0029 264.26));
1005
+ border-radius: 0.125rem;
1006
+ }
1007
+ }
1008
+
1009
+ & span {
1010
+ font-size: 0.625rem;
1011
+ color: var(--color-content-subtle, oklch(52.38% 0.017 264.26));
1012
+ pointer-events: none;
1013
+ user-select: none;
1014
+ }
1015
+ }
1016
+
1017
+ /* Gradient stop bar */
1018
+ & .gradient-layer {
1019
+ position: relative;
1020
+ width: 100%;
1021
+ height: 0.75rem;
1022
+ border-radius: 1rem;
1023
+ cursor: pointer;
1024
+ background: var(--color-field-surface, oklch(91.79% 0.0029 264.26));
1025
+ }
1026
+
1027
+ & .stop-handle {
1028
+ position: absolute;
1029
+ top: 50%;
1030
+ width: 1rem;
1031
+ height: 1rem;
1032
+ border-radius: 50%;
1033
+ border: 2px solid #ffffff;
1034
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15), 0 1px 3px rgba(0, 0, 0, 0.25);
1035
+ transform: translate(-50%, -50%);
1036
+ cursor: grab;
1037
+ z-index: 1;
1038
+ touch-action: none;
1039
+
1040
+ &.active {
1041
+ box-shadow: 0 0 0 2px var(--color-brand-content, #de6618), 0 1px 3px rgba(0, 0, 0, 0.25);
1042
+ }
1043
+
1044
+ &:active {
1045
+ cursor: grabbing;
1046
+ }
1047
+ }
1048
+
1049
+ /* Stop context menu */
1050
+ & .stop-context-menu {
1051
+ min-width: 140px;
1052
+ }
1053
+
1054
+ /* Eyedropper button */
1055
+ & .eyedropper {}
1056
+
1057
+ /* Swatches grid */
1058
+ & .swatches {
1059
+ display: flex;
1060
+ flex-wrap: wrap;
1061
+ gap: 0.25rem;
1062
+
1063
+ & [data-color] {
1064
+ width: 1.25rem;
1065
+ height: 1.25rem;
1066
+ border-radius: calc(var(--radius, 0.5rem) - 0.125rem);
1067
+ border: 1px solid var(--color-line, color-mix(in oklch, oklch(16.6% 0.026 267) 11%, transparent));
1068
+ cursor: pointer;
1069
+ transition: var(--transition, all .05s ease-in-out);
1070
+
1071
+ &:hover {
1072
+ scale: 1.15;
1073
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2)
1074
+ }
1075
+ }
1076
+ }
1077
+ }
1078
+
1079
+ /* Hide number input spin buttons inside colorpicker (high specificity, flat selectors) */
1080
+ menu[popover].colorpicker .layer-angle input[type=number]::-webkit-inner-spin-button,
1081
+ menu[popover].colorpicker .layer-angle input[type=number]::-webkit-outer-spin-button {
1082
+ -webkit-appearance: none !important;
1083
+ appearance: none !important;
1084
+ margin: 0 !important;
1085
+ display: none !important;
1086
+ width: 0 !important;
1087
+ height: 0 !important;
1088
+ }
1089
+ }
1090
+
693
1091
  /* Manifest Dialogs */
694
1092
 
695
1093
  @layer components {
@@ -975,7 +1373,7 @@
975
1373
  flex-shrink: 0;
976
1374
 
977
1375
  &:not(:first-child) {
978
- margin-top: var(--spacing, 0.25rem)
1376
+ /* margin-top: var(--spacing, 0.25rem) */
979
1377
  }
980
1378
  }
981
1379
 
@@ -1293,7 +1691,7 @@
1293
1691
 
1294
1692
  @layer components {
1295
1693
 
1296
- :where(input:not([type=range]), textarea, label:has([type=search], [type=file]), .label:has([type=search], [type=file])):not(.unstyle) {
1694
+ :where(input:not([type=range], [type=color]), textarea, label:has([type=search], [type=file]), .label:has([type=search], [type=file])):not(.unstyle) {
1297
1695
  width: 100%;
1298
1696
  max-width: 100%;
1299
1697
  color: var(--color-field-inverse, oklch(16.6% 0.026 267));
@@ -11760,20 +11760,51 @@ async function initializeDataSourcesPlugin() {
11760
11760
  _currentUrl: existingStore._currentUrl || window.location.pathname
11761
11761
  });
11762
11762
 
11763
- // Pre-load manifest and critical data sources so $x.content (etc.) is ready before components render
11763
+ // Pre-load all local file-backed data sources so $x.* accessors see
11764
+ // real data on the very first render pass.
11765
+ //
11766
+ // Each source is at most ONE fetch regardless of type:
11767
+ // - Simple string paths (e.g. "/data/clients.yaml") → one file.
11768
+ // - Localized objects (e.g. { "en": "...", "fr": "..." }) → only the
11769
+ // current locale file is fetched (+ default locale for fallback
11770
+ // merging if different), NOT all 35 variants.
11771
+ // - Single CSV with embedded locales → one file.
11772
+ //
11773
+ // Skipped (remain on-demand):
11774
+ // - Appwrite collections / buckets — require auth/session context.
11775
+ // - API-URL sources — may have side-effects or auth requirements.
11776
+ // - The special "manifest" key — handled separately below.
11777
+ //
11778
+ // Without pre-loading, sources like $x.clients load asynchronously on
11779
+ // first access, causing a visible flash in the SPA and missing data in
11780
+ // prerender snapshots.
11764
11781
  try {
11765
11782
  const manifest = await window.ManifestDataConfig.ensureManifest();
11766
11783
  const locale = (typeof document !== 'undefined' && document.documentElement?.lang) || (typeof Alpine !== 'undefined' && Alpine.store('locale')?.current) || 'en';
11784
+ const isAppwriteCollection = window.ManifestDataConfig.isAppwriteCollection;
11767
11785
 
11768
- // Pre-load content so first $x.content access (e.g. in header) sees data; avoids race with on-demand load
11769
- if (manifest?.data?.content) {
11770
- try {
11771
- const content = await loadDataSource('content', locale);
11772
- if (content != null && window.ManifestDataStore?.updateStore) {
11773
- window.ManifestDataStore.updateStore('content', content, { loading: false, error: null, ready: true, allowDuringInit: true });
11774
- }
11775
- } catch (contentErr) {
11776
- console.warn('[Manifest Data] Failed to pre-load content:', contentErr);
11786
+ if (manifest?.data) {
11787
+ const preloadNames = [];
11788
+ for (const [name, source] of Object.entries(manifest.data)) {
11789
+ if (name === 'manifest') continue; // handled separately below
11790
+ if (isAppwriteCollection(source)) continue;
11791
+ if (source && typeof source === 'object' && source.url) continue;
11792
+ preloadNames.push(name);
11793
+ }
11794
+
11795
+ if (preloadNames.length > 0) {
11796
+ await Promise.all(
11797
+ preloadNames.map(async (name) => {
11798
+ try {
11799
+ const data = await loadDataSource(name, locale);
11800
+ if (data != null && window.ManifestDataStore?.updateStore) {
11801
+ window.ManifestDataStore.updateStore(name, data, { loading: false, error: null, ready: true, allowDuringInit: true });
11802
+ }
11803
+ } catch (err) {
11804
+ console.warn(`[Manifest Data] Failed to pre-load ${name}:`, err);
11805
+ }
11806
+ })
11807
+ );
11777
11808
  }
11778
11809
  }
11779
11810
 
@@ -132,7 +132,7 @@
132
132
  flex-shrink: 0;
133
133
 
134
134
  &:not(:first-child) {
135
- margin-top: var(--spacing, 0.25rem)
135
+ /* margin-top: var(--spacing, 0.25rem) */
136
136
  }
137
137
  }
138
138
 
@@ -170,6 +170,12 @@ function initializeDropdownPlugin() {
170
170
  }
171
171
  };
172
172
 
173
+ // True when pointer is over an open Manifest tooltip (hint popover), e.g. after leaving the menu
174
+ const isOverOpenHintTooltip = () =>
175
+ Array.from(document.querySelectorAll('.tooltip:popover-open')).some((t) =>
176
+ t.matches(':hover')
177
+ );
178
+
173
179
  // Enhanced auto-close when mouse leaves both trigger and menu
174
180
  startAutoCloseTimer = () => {
175
181
  clearTimeout(autoCloseTimeout);
@@ -178,7 +184,7 @@ function initializeDropdownPlugin() {
178
184
  const isOverButton = el.matches(':hover');
179
185
  const isOverMenu = menu.matches(':hover');
180
186
 
181
- if (!isOverButton && !isOverMenu) {
187
+ if (!isOverButton && !isOverMenu && !isOverOpenHintTooltip()) {
182
188
  menu.hidePopover();
183
189
  }
184
190
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  @layer components {
4
4
 
5
- :where(input:not([type=range]), textarea, label:has([type=search], [type=file]), .label:has([type=search], [type=file])):not(.unstyle) {
5
+ :where(input:not([type=range], [type=color]), textarea, label:has([type=search], [type=file]), .label:has([type=search], [type=file])):not(.unstyle) {
6
6
  width: 100%;
7
7
  max-width: 100%;
8
8
  color: var(--color-field-inverse, oklch(16.6% 0.026 267));