adminforth 1.2.95 → 1.2.97
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.
- package/dataConnectors/baseConnector.ts +26 -14
- package/dataConnectors/clickhouse.ts +57 -37
- package/dataConnectors/mongo.ts +45 -14
- package/dataConnectors/postgres.ts +53 -36
- package/dataConnectors/sqlite.ts +37 -35
- package/dist/dataConnectors/baseConnector.js +16 -7
- package/dist/dataConnectors/clickhouse.js +58 -48
- package/dist/dataConnectors/mongo.js +29 -12
- package/dist/dataConnectors/postgres.js +62 -52
- package/dist/dataConnectors/sqlite.js +52 -45
- package/dist/index.js +5 -1
- package/dist/modules/operationalResource.js +88 -0
- package/dist/modules/restApi.js +27 -29
- package/dist/spa/index.html +4 -4
- package/dist/spa/package-lock.json +421 -6
- package/dist/spa/package.json +10 -2
- package/dist/spa/src/App.vue +236 -94
- package/dist/spa/src/assets/base.css +2 -0
- package/dist/spa/src/assets/logo.svg +19 -1
- package/dist/spa/src/components/AcceptModal.vue +3 -10
- package/dist/spa/src/components/Breadcrumbs.vue +3 -2
- package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
- package/dist/spa/src/components/CustomDatePicker.vue +25 -10
- package/dist/spa/src/components/CustomDateRangePicker.vue +14 -5
- package/dist/spa/src/components/Dropdown.vue +19 -5
- package/dist/spa/src/components/Filters.vue +103 -38
- package/dist/spa/src/components/MenuLink.vue +3 -3
- package/dist/spa/src/components/ResourceForm.vue +205 -116
- package/dist/spa/{spa/src → src}/components/ResourceListTable.vue +6 -3
- package/dist/spa/{spa/src → src}/components/Toast.vue +9 -3
- package/dist/spa/src/components/ValueRenderer.vue +30 -8
- package/dist/spa/src/index.scss +2 -1
- package/dist/spa/src/main.ts +2 -2
- package/dist/spa/src/router/index.ts +29 -19
- package/dist/spa/src/stores/core.ts +65 -59
- package/dist/spa/src/stores/modal.ts +13 -3
- package/dist/spa/src/types/AdminForthConfig.ts +1477 -0
- package/dist/spa/src/types/FrontendAPI.ts +121 -0
- package/dist/spa/src/utils.ts +69 -10
- package/dist/spa/src/views/CreateView.vue +69 -16
- package/dist/spa/src/views/EditView.vue +68 -14
- package/dist/spa/src/views/ListView.vue +100 -371
- package/dist/spa/src/views/LoginView.vue +67 -29
- package/dist/spa/src/views/ResourceParent.vue +1 -2
- package/dist/spa/src/views/ShowView.vue +116 -25
- package/dist/spa/tailwind.config.js +8 -3
- package/dist/spa/vite.config.ts +13 -0
- package/dist/types/AdminForthConfig.js +37 -0
- package/index.ts +12 -3
- package/modules/operationalResource.ts +97 -0
- package/modules/restApi.ts +31 -31
- package/package.json +1 -1
- package/types/AdminForthConfig.ts +91 -8
- package/dist/spa/.vscode/extensions.json +0 -6
- package/dist/spa/public/favicon.ico +0 -0
- package/dist/spa/spa/.eslintrc.cjs +0 -14
- package/dist/spa/spa/.vscode/extensions.json +0 -6
- package/dist/spa/spa/README.md +0 -39
- package/dist/spa/spa/env.d.ts +0 -1
- package/dist/spa/spa/index.html +0 -23
- package/dist/spa/spa/package-lock.json +0 -4573
- package/dist/spa/spa/package.json +0 -49
- package/dist/spa/spa/postcss.config.js +0 -6
- package/dist/spa/spa/public/favicon.ico +0 -0
- package/dist/spa/spa/src/App.vue +0 -314
- package/dist/spa/spa/src/assets/base.css +0 -0
- package/dist/spa/spa/src/assets/logo.svg +0 -19
- package/dist/spa/spa/src/components/AcceptModal.vue +0 -45
- package/dist/spa/spa/src/components/Breadcrumbs.vue +0 -40
- package/dist/spa/spa/src/components/BreadcrumbsWithButtons.vue +0 -26
- package/dist/spa/spa/src/components/CustomDatePicker.vue +0 -174
- package/dist/spa/spa/src/components/CustomDateRangePicker.vue +0 -218
- package/dist/spa/spa/src/components/Dropdown.vue +0 -168
- package/dist/spa/spa/src/components/Filters.vue +0 -222
- package/dist/spa/spa/src/components/HelloWorld.vue +0 -17
- package/dist/spa/spa/src/components/MenuLink.vue +0 -24
- package/dist/spa/spa/src/components/ResourceForm.vue +0 -289
- package/dist/spa/spa/src/components/SingleSkeletLoader.vue +0 -13
- package/dist/spa/spa/src/components/ValueRenderer.vue +0 -66
- package/dist/spa/spa/src/components/icons/IconCalendar.vue +0 -5
- package/dist/spa/spa/src/components/icons/IconCommunity.vue +0 -7
- package/dist/spa/spa/src/components/icons/IconDocumentation.vue +0 -7
- package/dist/spa/spa/src/components/icons/IconEcosystem.vue +0 -7
- package/dist/spa/spa/src/components/icons/IconSupport.vue +0 -7
- package/dist/spa/spa/src/components/icons/IconTime.vue +0 -5
- package/dist/spa/spa/src/components/icons/IconTooling.vue +0 -19
- package/dist/spa/spa/src/index.scss +0 -27
- package/dist/spa/spa/src/main.ts +0 -18
- package/dist/spa/spa/src/router/index.ts +0 -63
- package/dist/spa/spa/src/stores/core.ts +0 -144
- package/dist/spa/spa/src/stores/modal.ts +0 -48
- package/dist/spa/spa/src/utils.ts +0 -103
- package/dist/spa/spa/src/views/CreateView.vue +0 -156
- package/dist/spa/spa/src/views/EditView.vue +0 -157
- package/dist/spa/spa/src/views/HomeView.vue +0 -8
- package/dist/spa/spa/src/views/ListView.vue +0 -258
- package/dist/spa/spa/src/views/LoginView.vue +0 -143
- package/dist/spa/spa/src/views/ResourceParent.vue +0 -17
- package/dist/spa/spa/src/views/ShowView.vue +0 -184
- package/dist/spa/spa/tailwind.config.js +0 -17
- package/dist/spa/spa/tsconfig.app.json +0 -14
- package/dist/spa/spa/tsconfig.json +0 -11
- package/dist/spa/spa/tsconfig.node.json +0 -19
- package/dist/spa/spa/vite.config.ts +0 -56
- package/dist/spa/src/views/HomeView.vue +0 -8
- package/dist/types.js +0 -30
- /package/dist/spa/{spa/public → public}/assets/favicon.png +0 -0
- /package/dist/spa/{spa/src → src}/components/CustomRangePicker.vue +0 -0
- /package/dist/spa/{spa/src → src}/components/SkeleteLoader.vue +0 -0
- /package/dist/spa/{spa/src → src}/composables/useFrontendApi.ts +0 -0
- /package/dist/spa/{spa/src → src}/composables/useStores.ts +0 -0
- /package/dist/spa/{spa/src → src}/spa_types/core.ts +0 -0
- /package/dist/spa/{spa/src → src}/stores/filters.ts +0 -0
- /package/dist/spa/{spa/src → src}/stores/toast.ts +0 -0
- /package/dist/spa/{spa/src → src}/stores/user.ts +0 -0
package/modules/restApi.ts
CHANGED
|
@@ -21,6 +21,32 @@ import { ADMINFORTH_VERSION, listify } from './utils.js';
|
|
|
21
21
|
|
|
22
22
|
import AdminForthAuth from "../auth.js";
|
|
23
23
|
|
|
24
|
+
|
|
25
|
+
export async function interpretResource(adminUser: AdminUser, resource: AdminForthResource, meta: any, source: ActionCheckSource): Promise<{allowedActions: AllowedActionsResolved}> {
|
|
26
|
+
// if (process.env.HEAVY_DEBUG) {
|
|
27
|
+
// console.log('🪲Interpreting resource', resource.resourceId, source, 'adminUser', adminUser);
|
|
28
|
+
// }
|
|
29
|
+
const allowedActions = {};
|
|
30
|
+
|
|
31
|
+
await Promise.all(
|
|
32
|
+
Object.entries(resource.options?.allowedActions || {}).map(
|
|
33
|
+
async ([key, value]: [string, AllowedActionValue]) => {
|
|
34
|
+
if (process.env.HEAVY_DEBUG) {
|
|
35
|
+
console.log('🪲checking for allowed call', key, 'value:', value, 'typeof', typeof value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// if callable then call
|
|
39
|
+
if (typeof value === 'function') {
|
|
40
|
+
allowedActions[key] = await value({ adminUser, resource, meta, source });
|
|
41
|
+
} else {
|
|
42
|
+
allowedActions[key] = value;
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return { allowedActions };
|
|
48
|
+
}
|
|
49
|
+
|
|
24
50
|
export default class AdminForthRestAPI {
|
|
25
51
|
|
|
26
52
|
adminforth: IAdminForth;
|
|
@@ -188,13 +214,10 @@ export default class AdminForthRestAPI {
|
|
|
188
214
|
}
|
|
189
215
|
|
|
190
216
|
async function processMenuItem(menuItem) {
|
|
191
|
-
console.log('super hueta')
|
|
192
|
-
|
|
193
217
|
if (menuItem.badge) {
|
|
194
|
-
|
|
195
|
-
// if (typeof menuItem.badge !== 'string') {
|
|
218
|
+
if (typeof menuItem.badge === 'function') {
|
|
196
219
|
menuItem.badge = await menuItem.badge(adminUser);
|
|
197
|
-
|
|
220
|
+
}
|
|
198
221
|
}
|
|
199
222
|
}
|
|
200
223
|
let newMenu = []
|
|
@@ -214,12 +237,12 @@ export default class AdminForthRestAPI {
|
|
|
214
237
|
continue
|
|
215
238
|
}
|
|
216
239
|
}
|
|
217
|
-
processMenuItem(newChild)
|
|
240
|
+
await processMenuItem(newChild)
|
|
218
241
|
newChildren.push(newChild)
|
|
219
242
|
}
|
|
220
243
|
newMenuItem = {...newMenuItem, children: newChildren}
|
|
221
244
|
}
|
|
222
|
-
processMenuItem(newMenuItem)
|
|
245
|
+
await processMenuItem(newMenuItem)
|
|
223
246
|
newMenu.push(newMenuItem)
|
|
224
247
|
}
|
|
225
248
|
|
|
@@ -249,30 +272,7 @@ export default class AdminForthRestAPI {
|
|
|
249
272
|
},
|
|
250
273
|
});
|
|
251
274
|
|
|
252
|
-
|
|
253
|
-
// if (process.env.HEAVY_DEBUG) {
|
|
254
|
-
// console.log('🪲Interpreting resource', resource.resourceId, source, 'adminUser', adminUser);
|
|
255
|
-
// }
|
|
256
|
-
const allowedActions = {};
|
|
257
|
-
|
|
258
|
-
await Promise.all(
|
|
259
|
-
Object.entries(resource.options?.allowedActions || {}).map(
|
|
260
|
-
async ([key, value]: [string, AllowedActionValue]) => {
|
|
261
|
-
if (process.env.HEAVY_DEBUG) {
|
|
262
|
-
console.log('🪲checking for allowed call', key, 'value:', value, 'typeof', typeof value);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// if callable then call
|
|
266
|
-
if (typeof value === 'function') {
|
|
267
|
-
allowedActions[key] = await value({ adminUser, resource, meta, source });
|
|
268
|
-
} else {
|
|
269
|
-
allowedActions[key] = value;
|
|
270
|
-
}
|
|
271
|
-
})
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
return { allowedActions };
|
|
275
|
-
}
|
|
275
|
+
|
|
276
276
|
|
|
277
277
|
function checkAccess(action: AllowedActionsEnum, allowedActions: AllowedActions): { allowed: boolean, error?: string } {
|
|
278
278
|
const allowed = (allowedActions[action] as boolean | string | undefined);
|
package/package.json
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Express } from 'express';
|
|
2
|
+
import AdminForth from '../index.js';
|
|
3
|
+
import exp from 'constants';
|
|
2
4
|
|
|
3
5
|
export interface ICodeInjector {
|
|
4
6
|
srcFoldersToSync: Object;
|
|
@@ -90,6 +92,17 @@ export interface IExpressHttpServer extends IHttpServer {
|
|
|
90
92
|
authorize(callable: Function): void;
|
|
91
93
|
}
|
|
92
94
|
|
|
95
|
+
export interface IAdminForthFilter {
|
|
96
|
+
field: string;
|
|
97
|
+
operator: AdminForthFilterOperators;
|
|
98
|
+
value: any;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface IAdminForthSort {
|
|
102
|
+
field: string,
|
|
103
|
+
direction: AdminForthSortDirections
|
|
104
|
+
}
|
|
105
|
+
|
|
93
106
|
export interface IAdminForthDataSourceConnector {
|
|
94
107
|
|
|
95
108
|
/**
|
|
@@ -145,15 +158,21 @@ export interface IAdminForthDataSourceConnector {
|
|
|
145
158
|
*
|
|
146
159
|
* Fields are returned from db "as is" then {@link AdminForthBaseConnector.getData} will transform each field using {@link IAdminForthDataSourceConnector.getFieldValue}
|
|
147
160
|
*/
|
|
148
|
-
getDataWithOriginalTypes({ resource, limit, offset, sort, filters
|
|
161
|
+
getDataWithOriginalTypes({ resource, limit, offset, sort, filters }: {
|
|
149
162
|
resource: AdminForthResource,
|
|
150
163
|
limit: number,
|
|
151
164
|
offset: number,
|
|
152
|
-
sort:
|
|
153
|
-
filters:
|
|
154
|
-
|
|
155
|
-
}): Promise<{data: Array<any>, total: number}>;
|
|
165
|
+
sort: IAdminForthSort[],
|
|
166
|
+
filters: IAdminForthFilter[],
|
|
167
|
+
}): Promise<Array<any>>;
|
|
156
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Used to get count of records in database.
|
|
171
|
+
*/
|
|
172
|
+
getCount({ resource, filters }: {
|
|
173
|
+
resource: AdminForthResource,
|
|
174
|
+
filters: IAdminForthFilter[],
|
|
175
|
+
}): Promise<number>;
|
|
157
176
|
|
|
158
177
|
/**
|
|
159
178
|
* Optional method which used to get min and max values for columns in resource.
|
|
@@ -181,7 +200,7 @@ export interface IAdminForthDataSourceConnector {
|
|
|
181
200
|
/**
|
|
182
201
|
* Used to delete record in database.
|
|
183
202
|
*/
|
|
184
|
-
deleteRecord({ resource, recordId }: { resource: AdminForthResource, recordId: any }): Promise<
|
|
203
|
+
deleteRecord({ resource, recordId }: { resource: AdminForthResource, recordId: any }): Promise<boolean>;
|
|
185
204
|
}
|
|
186
205
|
|
|
187
206
|
|
|
@@ -196,12 +215,15 @@ export interface IAdminForthDataSourceConnectorBase extends IAdminForthDataSourc
|
|
|
196
215
|
resource: AdminForthResource,
|
|
197
216
|
limit: number,
|
|
198
217
|
offset: number,
|
|
199
|
-
sort:
|
|
200
|
-
filters:
|
|
218
|
+
sort: IAdminForthSort[],
|
|
219
|
+
filters: IAdminForthFilter[],
|
|
220
|
+
getTotals?: boolean,
|
|
201
221
|
}): Promise<{ data: Array<any>, total: number }>;
|
|
202
222
|
|
|
203
223
|
getRecordByPrimaryKey(resource: AdminForthResource, recordId: string): Promise<any>;
|
|
204
224
|
|
|
225
|
+
createRecord({ resource, record }: { resource: AdminForthResource, record: any }): Promise<void>;
|
|
226
|
+
|
|
205
227
|
getMinMaxForColumns({ resource, columns }: { resource: AdminForthResource, columns: AdminForthResourceColumn[] }): Promise<{ [key: string]: { min: any, max: any } }>;
|
|
206
228
|
}
|
|
207
229
|
|
|
@@ -1306,6 +1328,67 @@ export type AdminForthConfig = {
|
|
|
1306
1328
|
|
|
1307
1329
|
|
|
1308
1330
|
}
|
|
1331
|
+
|
|
1332
|
+
// define typescript objects which I can instantiate as Filters.EQ(field, value) and they woudl
|
|
1333
|
+
// return { field: field, operator: 'eq', value: value }. They should be exported with Filters namespace so I can import Filters from this file
|
|
1334
|
+
// and use Filters.EQ(field, value) in my code
|
|
1335
|
+
|
|
1336
|
+
export type FDataFilter = (field: string, value: any) => IAdminForthFilter;
|
|
1337
|
+
|
|
1338
|
+
export class Filters {
|
|
1339
|
+
static EQ(field: string, value: any): IAdminForthFilter {
|
|
1340
|
+
return { field, operator: AdminForthFilterOperators.EQ, value };
|
|
1341
|
+
}
|
|
1342
|
+
static NEQ(field: string, value: any): IAdminForthFilter {
|
|
1343
|
+
return { field, operator: AdminForthFilterOperators.NE, value };
|
|
1344
|
+
}
|
|
1345
|
+
static GT(field: string, value: any): IAdminForthFilter {
|
|
1346
|
+
return { field, operator: AdminForthFilterOperators.GT, value };
|
|
1347
|
+
}
|
|
1348
|
+
static GTE(field: string, value: any): IAdminForthFilter {
|
|
1349
|
+
return { field, operator: AdminForthFilterOperators.GTE, value };
|
|
1350
|
+
}
|
|
1351
|
+
static LT(field: string, value: any): IAdminForthFilter {
|
|
1352
|
+
return { field, operator: AdminForthFilterOperators.LT, value };
|
|
1353
|
+
}
|
|
1354
|
+
static LTE(field: string, value: any): IAdminForthFilter {
|
|
1355
|
+
return { field, operator: AdminForthFilterOperators.LTE, value };
|
|
1356
|
+
}
|
|
1357
|
+
static IN(field: string, value: any): IAdminForthFilter {
|
|
1358
|
+
return { field, operator: AdminForthFilterOperators.IN, value };
|
|
1359
|
+
}
|
|
1360
|
+
static NOT_IN(field: string, value: any): IAdminForthFilter {
|
|
1361
|
+
return { field, operator: AdminForthFilterOperators.NIN, value };
|
|
1362
|
+
}
|
|
1363
|
+
static LIKE(field: string, value: any): IAdminForthFilter {
|
|
1364
|
+
return { field, operator: AdminForthFilterOperators.LIKE, value };
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
export type FDataSort = (field: string, direction: AdminForthSortDirections) => IAdminForthSort;
|
|
1369
|
+
|
|
1370
|
+
export class Sorts {
|
|
1371
|
+
static ASC(field: string): IAdminForthSort {
|
|
1372
|
+
return { field, direction: AdminForthSortDirections.asc };
|
|
1373
|
+
}
|
|
1374
|
+
static DESC(field: string): IAdminForthSort {
|
|
1375
|
+
return { field, direction: AdminForthSortDirections.desc };
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
export interface IOperationalResource {
|
|
1380
|
+
get: (filter: IAdminForthFilter | IAdminForthFilter[]) => Promise<any[]>;
|
|
1381
|
+
|
|
1382
|
+
list: (filter: IAdminForthFilter | IAdminForthFilter[], limit: number, offset: number, sort: IAdminForthSort | IAdminForthSort[]) => Promise<any[]>;
|
|
1383
|
+
|
|
1384
|
+
count: (filter: IAdminForthFilter | IAdminForthFilter[]) => Promise<number>;
|
|
1385
|
+
|
|
1386
|
+
create: (record: any) => Promise<any>;
|
|
1387
|
+
|
|
1388
|
+
update: (primaryKey: any, record: any) => Promise<any>;
|
|
1389
|
+
|
|
1390
|
+
delete: (primaryKey: any) => Promise<boolean>;
|
|
1391
|
+
}
|
|
1309
1392
|
|
|
1310
1393
|
|
|
1311
1394
|
export enum AllowedActionsEnum {
|
|
Binary file
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/* eslint-env node */
|
|
2
|
-
require('@rushstack/eslint-patch/modern-module-resolution')
|
|
3
|
-
|
|
4
|
-
module.exports = {
|
|
5
|
-
root: true,
|
|
6
|
-
'extends': [
|
|
7
|
-
'plugin:vue/vue3-essential',
|
|
8
|
-
'eslint:recommended',
|
|
9
|
-
'@vue/eslint-config-typescript'
|
|
10
|
-
],
|
|
11
|
-
parserOptions: {
|
|
12
|
-
ecmaVersion: 'latest'
|
|
13
|
-
}
|
|
14
|
-
}
|
package/dist/spa/spa/README.md
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# spa
|
|
2
|
-
|
|
3
|
-
This template should help get you started developing with Vue 3 in Vite.
|
|
4
|
-
|
|
5
|
-
## Recommended IDE Setup
|
|
6
|
-
|
|
7
|
-
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
|
8
|
-
|
|
9
|
-
## Type Support for `.vue` Imports in TS
|
|
10
|
-
|
|
11
|
-
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
|
|
12
|
-
|
|
13
|
-
## Customize configuration
|
|
14
|
-
|
|
15
|
-
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
|
16
|
-
|
|
17
|
-
## Project Setup
|
|
18
|
-
|
|
19
|
-
```sh
|
|
20
|
-
npm install
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
### Compile and Hot-Reload for Development
|
|
24
|
-
|
|
25
|
-
```sh
|
|
26
|
-
npm run dev
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### Type-Check, Compile and Minify for Production
|
|
30
|
-
|
|
31
|
-
```sh
|
|
32
|
-
npm run build
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Lint with [ESLint](https://eslint.org/)
|
|
36
|
-
|
|
37
|
-
```sh
|
|
38
|
-
npm run lint
|
|
39
|
-
```
|
package/dist/spa/spa/env.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/// <reference types="vite/client" />
|
package/dist/spa/spa/index.html
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<link rel="icon" href="/* IMPORTANT:ADMINFORTH FAVICON */">
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<title>/* IMPORTANT:ADMINFORTH TITLE */</title>
|
|
8
|
-
<!--
|
|
9
|
-
<script>
|
|
10
|
-
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
|
|
11
|
-
if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
|
12
|
-
document.documentElement.classList.add('dark');
|
|
13
|
-
} else {
|
|
14
|
-
document.documentElement.classList.remove('dark')
|
|
15
|
-
}
|
|
16
|
-
</script> -->
|
|
17
|
-
|
|
18
|
-
</head>
|
|
19
|
-
<body class=" ">
|
|
20
|
-
<div id="app" class="min-h-screen bg-lightHtml dark:bg-darkHtml"></div>
|
|
21
|
-
<script type="module" src="/src/main.ts"></script>
|
|
22
|
-
</body>
|
|
23
|
-
</html>
|