lula2 0.0.5 → 0.0.6

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 (108) hide show
  1. package/README.md +291 -8
  2. package/dist/_app/env.js +1 -0
  3. package/dist/_app/immutable/assets/0.DtiRW3lO.css +1 -0
  4. package/dist/_app/immutable/assets/DynamicControlEditor.BkVTzFZ-.css +1 -0
  5. package/dist/_app/immutable/chunks/7x_q-1ab.js +1 -0
  6. package/dist/_app/immutable/chunks/B19gt6-g.js +2 -0
  7. package/dist/_app/immutable/chunks/BR-0Dorr.js +1 -0
  8. package/dist/_app/immutable/chunks/B_3ksxz5.js +2 -0
  9. package/dist/_app/immutable/chunks/Bg_R1qWi.js +3 -0
  10. package/dist/_app/immutable/chunks/D3aNP_lg.js +1 -0
  11. package/dist/_app/immutable/chunks/D4Q_ObIy.js +1 -0
  12. package/dist/_app/immutable/chunks/DsnmJJEf.js +1 -0
  13. package/dist/_app/immutable/chunks/XY2j_owG.js +66 -0
  14. package/dist/_app/immutable/chunks/rzN25oDf.js +1 -0
  15. package/dist/_app/immutable/entry/app.r0uOd9qg.js +2 -0
  16. package/dist/_app/immutable/entry/start.DvoqR0rc.js +1 -0
  17. package/dist/_app/immutable/nodes/0.Ct6FAss_.js +1 -0
  18. package/dist/_app/immutable/nodes/1.DLoKuy8Q.js +1 -0
  19. package/dist/_app/immutable/nodes/2.IRkwSmiB.js +1 -0
  20. package/dist/_app/immutable/nodes/3.BrTg-ZHv.js +1 -0
  21. package/dist/_app/immutable/nodes/4.Blq-4WQS.js +9 -0
  22. package/dist/_app/version.json +1 -0
  23. package/dist/cli/commands/crawl.js +128 -0
  24. package/dist/cli/commands/ui.js +2769 -0
  25. package/dist/cli/commands/version.js +30 -0
  26. package/dist/cli/server/index.js +2713 -0
  27. package/dist/cli/server/server.js +2702 -0
  28. package/dist/cli/server/serverState.js +1199 -0
  29. package/dist/cli/server/spreadsheetRoutes.js +788 -0
  30. package/dist/cli/server/types.js +0 -0
  31. package/dist/cli/server/websocketServer.js +2625 -0
  32. package/dist/cli/utils/debug.js +24 -0
  33. package/dist/favicon.svg +1 -0
  34. package/dist/index.html +38 -0
  35. package/dist/index.js +2924 -37
  36. package/dist/lula.png +0 -0
  37. package/dist/lula2 +2 -0
  38. package/package.json +120 -72
  39. package/src/app.css +192 -0
  40. package/src/app.d.ts +13 -0
  41. package/src/app.html +13 -0
  42. package/src/lib/actions/fadeWhenScrollable.ts +39 -0
  43. package/src/lib/actions/modal.ts +230 -0
  44. package/src/lib/actions/tooltip.ts +82 -0
  45. package/src/lib/components/control-sets/ControlSetInfo.svelte +20 -0
  46. package/src/lib/components/control-sets/ControlSetSelector.svelte +46 -0
  47. package/src/lib/components/control-sets/index.ts +5 -0
  48. package/src/lib/components/controls/ControlDetailsPanel.svelte +235 -0
  49. package/src/lib/components/controls/ControlsList.svelte +608 -0
  50. package/src/lib/components/controls/DynamicControlEditor.svelte +298 -0
  51. package/src/lib/components/controls/MappingCard.svelte +105 -0
  52. package/src/lib/components/controls/MappingForm.svelte +188 -0
  53. package/src/lib/components/controls/index.ts +9 -0
  54. package/src/lib/components/controls/renderers/EditableFieldRenderer.svelte +103 -0
  55. package/src/lib/components/controls/renderers/FieldRenderer.svelte +49 -0
  56. package/src/lib/components/controls/renderers/index.ts +5 -0
  57. package/src/lib/components/controls/tabs/CustomFieldsTab.svelte +130 -0
  58. package/src/lib/components/controls/tabs/ImplementationTab.svelte +127 -0
  59. package/src/lib/components/controls/tabs/MappingsTab.svelte +182 -0
  60. package/src/lib/components/controls/tabs/OverviewTab.svelte +151 -0
  61. package/src/lib/components/controls/tabs/TimelineTab.svelte +41 -0
  62. package/src/lib/components/controls/tabs/index.ts +8 -0
  63. package/src/lib/components/controls/utils/ProcessedTextRenderer.svelte +63 -0
  64. package/src/lib/components/controls/utils/textProcessor.ts +164 -0
  65. package/src/lib/components/forms/DynamicControlForm.svelte +340 -0
  66. package/src/lib/components/forms/DynamicField.svelte +494 -0
  67. package/src/lib/components/forms/FormField.svelte +107 -0
  68. package/src/lib/components/forms/index.ts +6 -0
  69. package/src/lib/components/setup/ExistingControlSets.svelte +284 -0
  70. package/src/lib/components/setup/SpreadsheetImport.svelte +968 -0
  71. package/src/lib/components/setup/index.ts +5 -0
  72. package/src/lib/components/ui/Dropdown.svelte +107 -0
  73. package/src/lib/components/ui/EmptyState.svelte +80 -0
  74. package/src/lib/components/ui/FeatureToggle.svelte +50 -0
  75. package/src/lib/components/ui/SearchBar.svelte +73 -0
  76. package/src/lib/components/ui/StatusBadge.svelte +79 -0
  77. package/src/lib/components/ui/TabNavigation.svelte +48 -0
  78. package/src/lib/components/ui/Tooltip.svelte +120 -0
  79. package/src/lib/components/ui/index.ts +10 -0
  80. package/src/lib/components/version-control/DiffViewer.svelte +292 -0
  81. package/src/lib/components/version-control/TimelineItem.svelte +107 -0
  82. package/src/lib/components/version-control/YamlDiffViewer.svelte +428 -0
  83. package/src/lib/components/version-control/index.ts +6 -0
  84. package/src/lib/form-types.ts +57 -0
  85. package/src/lib/formatUtils.ts +17 -0
  86. package/src/lib/index.ts +5 -0
  87. package/src/lib/types.ts +180 -0
  88. package/src/lib/websocket.ts +359 -0
  89. package/src/routes/+layout.svelte +236 -0
  90. package/src/routes/+page.svelte +38 -0
  91. package/src/routes/control/[id]/+page.svelte +112 -0
  92. package/src/routes/setup/+page.svelte +241 -0
  93. package/src/stores/compliance.ts +95 -0
  94. package/src/styles/highlightjs.css +20 -0
  95. package/src/styles/modal.css +58 -0
  96. package/src/styles/tables.css +111 -0
  97. package/src/styles/tooltip.css +65 -0
  98. package/dist/controls/index.d.ts +0 -18
  99. package/dist/controls/index.d.ts.map +0 -1
  100. package/dist/controls/index.js +0 -18
  101. package/dist/crawl.d.ts +0 -62
  102. package/dist/crawl.d.ts.map +0 -1
  103. package/dist/crawl.js +0 -172
  104. package/dist/index.d.ts +0 -8
  105. package/dist/index.d.ts.map +0 -1
  106. package/src/controls/index.ts +0 -19
  107. package/src/crawl.ts +0 -227
  108. package/src/index.ts +0 -46
package/dist/lula.png ADDED
Binary file
package/dist/lula2 ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import './index.js';
package/package.json CHANGED
@@ -1,74 +1,122 @@
1
1
  {
2
- "name": "lula2",
3
- "version": "0.0.5",
4
- "description": "A tool for managing compliance as code in your GitHub repositories.",
5
- "bin": "./dist/index.js",
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "type": "module",
9
- "engines": {
10
- "node": ">=22.0.0"
11
- },
12
- "repository": {
13
- "type": "git",
14
- "url": "git+https://github.com/defenseunicorns/lula.git"
15
- },
16
- "keywords": [
17
- "compliance",
18
- "devops",
19
- "devsecops"
20
- ],
21
- "author": "Defense Unicorns",
22
- "license": "Apache-2.0",
23
- "bugs": {
24
- "url": "https://github.com/defenseunicorns/lula/issues"
25
- },
26
- "homepage": "https://github.com/defenseunicorns/lula#readme",
27
- "files": [
28
- "/src",
29
- "/dist",
30
- "!src/**/*.test.ts",
31
- "!dist/**/*.test.js*",
32
- "!dist/**/*.test.d.ts*"
33
- ],
34
- "scripts": {
35
- "prebuild": "rm -rf dist",
36
- "build": "tsc",
37
- "semantic-release": "semantic-release",
38
- "format:check": "eslint src && prettier . --check",
39
- "format:fix": "eslint --fix src && prettier . --write",
40
- "prepare": "if [ \"$NODE_ENV\" != 'production' ]; then husky; fi",
41
- "test": "vitest run --coverage"
42
- },
43
- "dependencies": {
44
- "@octokit/rest": "^22.0.0",
45
- "commander": "^14.0.0",
46
- "compliance-reporter": "^0.1.2",
47
- "undici": "^7.13.0"
48
- },
49
- "devDependencies": {
50
- "@commitlint/cli": "^19.8.0",
51
- "@commitlint/config-conventional": "^19.8.0",
52
- "@eslint/js": "^9.23.0",
53
- "@typescript-eslint/eslint-plugin": "^8.41.0",
54
- "@typescript-eslint/parser": "^8.41.0",
55
- "@vitest/coverage-v8": "^3.2.4",
56
- "esbuild": "^0.25.1",
57
- "eslint": "^9.26.0",
58
- "eslint-config-prettier": "^10.0.2",
59
- "eslint-plugin-jsdoc": "^54.1.1",
60
- "globals": "^16.0.0",
61
- "husky": "^9.1.7",
62
- "prettier": "3.6.2",
63
- "semantic-release": "^24.2.3",
64
- "typescript": "5.9.2",
65
- "typescript-eslint": "^8.41.0",
66
- "vitest": "^3.2.4"
67
- },
68
- "release": {
69
- "branches": [
70
- "main",
71
- "next"
72
- ]
73
- }
2
+ "name": "lula2",
3
+ "version": "0.0.6",
4
+ "description": "A tool for managing compliance as code in your GitHub repositories.",
5
+ "bin": {
6
+ "lula2": "./dist/lula2"
7
+ },
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "type": "module",
11
+ "engines": {
12
+ "node": ">=22.0.0"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/defenseunicorns/lula-next.git"
17
+ },
18
+ "keywords": [
19
+ "compliance",
20
+ "devops",
21
+ "devsecops"
22
+ ],
23
+ "author": "Defense Unicorns",
24
+ "license": "Apache-2.0",
25
+ "bugs": {
26
+ "url": "https://github.com/defenseunicorns/lula-next/issues"
27
+ },
28
+ "homepage": "https://github.com/defenseunicorns/lula-next#readme",
29
+ "files": [
30
+ "/src",
31
+ "/dist",
32
+ "!src/**/*.test.ts",
33
+ "!dist/**/*.test.js*",
34
+ "!dist/**/*.test.d.ts*"
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'",
49
+ "format:check": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts'",
50
+ "lint": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.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
+ "dependencies": {
56
+ "@octokit/rest": "^22.0.0",
57
+ "@types/ws": "^8.18.1",
58
+ "commander": "^14.0.0",
59
+ "cors": "^2.8.5",
60
+ "csv-parse": "^6.1.0",
61
+ "exceljs": "^4.4.0",
62
+ "express": "^4.21.2",
63
+ "flowbite": "^2.5.2",
64
+ "glob": "^10.4.5",
65
+ "isomorphic-git": "^1.33.1",
66
+ "js-yaml": "^4.1.0",
67
+ "multer": "^2.0.2",
68
+ "open": "^9.1.0",
69
+ "undici": "^7.15.0",
70
+ "ws": "^8.18.3",
71
+ "yaml": "^2.8.1"
72
+ },
73
+ "devDependencies": {
74
+ "@commitlint/cli": "^19.8.1",
75
+ "@commitlint/config-conventional": "^19.8.1",
76
+ "@eslint/compat": "^1.3.2",
77
+ "@eslint/js": "^9.35.0",
78
+ "@sveltejs/adapter-static": "^3.0.9",
79
+ "@sveltejs/kit": "^2.37.1",
80
+ "@sveltejs/vite-plugin-svelte": "^6.1.4",
81
+ "@tailwindcss/vite": "^4.1.13",
82
+ "@types/cors": "^2.8.19",
83
+ "@types/express": "^4.17.23",
84
+ "@types/js-yaml": "^4.0.9",
85
+ "@types/multer": "^2.0.0",
86
+ "@typescript-eslint/eslint-plugin": "^8.42.0",
87
+ "@typescript-eslint/parser": "^8.42.0",
88
+ "@vitest/browser": "^3.2.4",
89
+ "@vitest/coverage-v8": "^3.2.4",
90
+ "carbon-icons-svelte": "^13.5.0",
91
+ "carbon-preprocess-svelte": "^0.11.11",
92
+ "concurrently": "^9.2.1",
93
+ "esbuild": "^0.25.9",
94
+ "eslint": "^9.35.0",
95
+ "eslint-config-prettier": "^10.1.8",
96
+ "eslint-plugin-jsdoc": "^54.5.0",
97
+ "eslint-plugin-svelte": "^3.12.2",
98
+ "globals": "^16.3.0",
99
+ "husky": "^9.1.7",
100
+ "playwright": "^1.55.0",
101
+ "prettier": "3.6.2",
102
+ "prettier-plugin-svelte": "^3.4.0",
103
+ "semantic-release": "^24.2.7",
104
+ "shellcheck": "^4.1.0",
105
+ "svelte": "^5.38.7",
106
+ "svelte-check": "^4.3.1",
107
+ "tailwind-merge": "^3.3.1",
108
+ "tailwindcss": "^4.1.13",
109
+ "tsx": "^4.20.5",
110
+ "typescript": "5.9.2",
111
+ "typescript-eslint": "^8.42.0",
112
+ "vite": "^7.1.4",
113
+ "vitest": "^3.2.4",
114
+ "vitest-browser-svelte": "^0.1.0"
115
+ },
116
+ "release": {
117
+ "branches": [
118
+ "main",
119
+ "next"
120
+ ]
121
+ }
74
122
  }
package/src/app.css ADDED
@@ -0,0 +1,192 @@
1
+ /*
2
+ Copyright 2025 Defense Unicorns
3
+ SPDX-License-Identifier: LicenseRef-Defense-Unicorns-Commercial
4
+ */
5
+
6
+ @import 'tailwindcss';
7
+ @plugin 'flowbite/plugin';
8
+
9
+ :root {
10
+ --navbar-height: 4rem;
11
+ --sidebar-width: 16rem;
12
+ --table-height: 350px;
13
+ }
14
+
15
+ @theme {
16
+ --breakpoint-xs: 31rem;
17
+ }
18
+
19
+ @layer base {
20
+ html {
21
+ @apply overflow-y-auto;
22
+
23
+ color-scheme: dark; /* Dark scrollbar color for Safari, the webkit overrides do not work */
24
+ /* Standard scrollbar properties (Firefox) */
25
+ scrollbar-width: thin;
26
+ scrollbar-color: rgba(156, 163, 175, 0.5) transparent;
27
+
28
+ /* WebKit scrollbar styling (Chrome, Edge) */
29
+ &::-webkit-scrollbar {
30
+ width: 5px; /* Width that's narrow but still usable */
31
+ z-index: 20;
32
+ }
33
+
34
+ /* Scrollbar thumb styling */
35
+ &::-webkit-scrollbar-thumb {
36
+ border-radius: 4px; /* Rounded corners for better aesthetics */
37
+ background-color: rgba(156, 163, 175, 0.5); /* Semi-transparent gray */
38
+ }
39
+
40
+ /* Transparent track to focus attention on the thumb */
41
+ &::-webkit-scrollbar-track {
42
+ background: transparent;
43
+ }
44
+
45
+ button:not([disabled]),
46
+ [role='button']:not([disabled]) {
47
+ cursor: pointer;
48
+ }
49
+ }
50
+ }
51
+
52
+ @layer components {
53
+ .sticky-header {
54
+ position: sticky;
55
+ z-index: 40;
56
+ }
57
+
58
+ /* Global button styling */
59
+ .btn {
60
+ @apply inline-flex cursor-pointer items-center rounded-lg border border-gray-600 bg-transparent px-3 py-1.5 text-center text-sm font-medium text-gray-400 transition-all ease-in-out hover:bg-gray-700 hover:text-white;
61
+ }
62
+
63
+ .btn-primary {
64
+ @apply cursor-pointer rounded-lg border border-blue-600 bg-gray-900 px-5 py-2.5 text-sm font-medium text-blue-500 transition-colors hover:bg-blue-600 hover:text-white focus:outline-none focus-visible:z-10 focus-visible:bg-blue-600 focus-visible:text-white focus-visible:ring-4 focus-visible:ring-blue-200 disabled:cursor-not-allowed disabled:border-transparent disabled:bg-gray-500 disabled:text-white;
65
+ }
66
+
67
+ .btn-secondary {
68
+ @apply cursor-pointer rounded-lg border border-transparent bg-blue-600 px-5 py-2.5 text-sm font-medium text-white transition-colors hover:bg-blue-700 focus:outline-none focus-visible:z-10 focus-visible:bg-blue-500 focus-visible:text-white focus-visible:ring-4 focus-visible:ring-blue-200 disabled:cursor-not-allowed disabled:border-transparent disabled:bg-gray-500 disabled:text-white;
69
+ }
70
+
71
+ .btn-alternative {
72
+ @apply cursor-pointer rounded-lg border border-gray-600 bg-gray-800 px-5 py-2.5 text-sm font-medium text-gray-400 transition-colors hover:bg-gray-700 hover:text-white focus:z-10 focus:outline-none focus-visible:ring-4 focus-visible:ring-gray-700 disabled:cursor-not-allowed disabled:bg-gray-800 disabled:text-gray-400;
73
+ }
74
+
75
+ .card {
76
+ @apply mb-4 rounded-lg border border-gray-800 bg-gray-900 p-4 shadow;
77
+ }
78
+
79
+ .search-input {
80
+ @apply block w-full rounded-lg border border-gray-600 bg-gray-800 p-2.5 pr-10 pl-10 text-sm text-gray-100 placeholder-gray-300 focus:border-blue-500 focus:text-white focus:ring-1 focus:ring-blue-500 focus:outline-none [&::-webkit-search-cancel-button]:hidden;
81
+ }
82
+
83
+ select {
84
+ @apply block w-full rounded-lg border border-gray-700 bg-gray-800 p-2.5 text-sm text-gray-100 placeholder-gray-400 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none;
85
+ }
86
+
87
+ svg.icon {
88
+ @apply flex-shrink-0 text-gray-400 transition duration-300;
89
+ }
90
+
91
+ .text-link {
92
+ @apply text-blue-400 decoration-blue-400 hover:underline;
93
+ }
94
+
95
+ .no-scrollbar {
96
+ scrollbar-width: none;
97
+ }
98
+ }
99
+
100
+ @utility scrollable-container {
101
+ overflow-x: hidden;
102
+ overflow-y: auto;
103
+ padding-bottom: 1rem;
104
+
105
+ position: static !important;
106
+ scrollbar-width: thin; /* duplication required here despite global override */
107
+
108
+ &:not(.no-fade) {
109
+ /* Create a fade overlay at the bottom */
110
+
111
+ &::after {
112
+ content: '';
113
+ @apply pointer-events-none absolute right-0 bottom-0 left-0;
114
+ height: 28px;
115
+ @apply bg-gradient-to-b from-gray-900/0 to-gray-900;
116
+ }
117
+ }
118
+
119
+ /* Specific styling for UI elements inside scrollable containers */
120
+
121
+ .btn {
122
+ &:not(.keep-default-btn-style) {
123
+ @apply border-none;
124
+ }
125
+ }
126
+
127
+ table {
128
+ @apply w-full table-fixed text-left text-sm text-gray-400;
129
+
130
+ thead {
131
+ @apply sticky top-0 z-10 text-xs text-gray-400 uppercase shadow-sm;
132
+
133
+ tr {
134
+ @apply bg-gray-900/30 text-left text-xs font-medium tracking-wider text-gray-400 uppercase;
135
+
136
+ th {
137
+ @apply bg-gray-800 px-2 py-4 whitespace-nowrap;
138
+ }
139
+
140
+ th:first-child {
141
+ @apply rounded-tl-lg;
142
+ }
143
+
144
+ th:last-child {
145
+ @apply rounded-tr-lg;
146
+ }
147
+ }
148
+ }
149
+
150
+ tbody {
151
+ tr {
152
+ @apply bg-gray-900;
153
+
154
+ &.group-boundary {
155
+ @apply border-t-5 border-t-gray-800;
156
+ }
157
+
158
+ &.expandable-row {
159
+ td {
160
+ @apply bg-gray-950;
161
+ }
162
+ }
163
+
164
+ td {
165
+ @apply whitespace-nowrap text-gray-400;
166
+ }
167
+ }
168
+ }
169
+ }
170
+ }
171
+
172
+ /* Create a fade overlay at the bottom for catalog views */
173
+ @utility fade-overlay {
174
+ position: sticky;
175
+ right: 0;
176
+ bottom: 0;
177
+ left: 0;
178
+ z-index: 10;
179
+ pointer-events: none;
180
+ height: 32px;
181
+ @apply bg-gradient-to-b from-gray-950/0 to-gray-950;
182
+ }
183
+
184
+ @custom-variant mobileLandscape {
185
+ @media only screen and ((
186
+ ((max-height: 990px) and (max-width: 640px)) or
187
+ ((max-height: 640px) and (max-width: 990px))
188
+ )
189
+ and (orientation: landscape)) {
190
+ @slot;
191
+ }
192
+ }
package/src/app.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ // See https://svelte.dev/docs/kit/types#app.d.ts
2
+ // for information about these interfaces
3
+ declare global {
4
+ namespace App {
5
+ // interface Error {}
6
+ // interface Locals {}
7
+ // interface PageData {}
8
+ // interface PageState {}
9
+ // interface Platform {}
10
+ }
11
+ }
12
+
13
+ export {};
package/src/app.html ADDED
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <!-- Set background color to match bg-gray-950 before styles are available -->
3
+ <html class="dark" lang="en" style="background-color: oklch(0.13 0.028 261.692)">
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <link rel="icon" href="%sveltekit.assets%/lula.png" />
7
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
8
+ %sveltekit.head%
9
+ </head>
10
+ <body data-sveltekit-preload-data="hover">
11
+ <div style="display: contents">%sveltekit.body%</div>
12
+ </body>
13
+ </html>
@@ -0,0 +1,39 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Lula Authors
3
+
4
+ // Action to apply or remove the 'no-fade' class based on when an element is scrollable (overflow-y-scroll)
5
+ // Our 'scrollable-container' class uses a CSS pseudo-element to apply a fade effect at the bottom of the container
6
+ // The 'no-fade' class removes that effect
7
+ export function fadeWhenScrollable(node: HTMLElement) {
8
+ // `check` adds “no-fade” if there is NO vertical overflow,
9
+ // removes it once overflow appears.
10
+ function check() {
11
+ if (node.scrollHeight > node.clientHeight) {
12
+ node.classList.remove('no-fade');
13
+ } else {
14
+ node.classList.add('no-fade');
15
+ }
16
+ }
17
+
18
+ // 1) watch for size/box changes (e.g. window resize, parent CSS changes, etc.)
19
+ const resizeObs = new ResizeObserver(check);
20
+ resizeObs.observe(node);
21
+
22
+ // 2) watch for DOM mutations under `node` (e.g. new rows inserted into a table)
23
+ const mutationObs = new MutationObserver(check);
24
+ mutationObs.observe(node, { childList: true, subtree: true });
25
+
26
+ // 3) also re-check on window resize
27
+ window.addEventListener('resize', check);
28
+
29
+ // initial check
30
+ check();
31
+
32
+ return {
33
+ destroy() {
34
+ resizeObs.disconnect();
35
+ mutationObs.disconnect();
36
+ window.removeEventListener('resize', check);
37
+ }
38
+ };
39
+ }