akanjs 2.4.2-rc.2 → 3.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/base/baseEnv.ts +0 -6
- package/client/cn.ts +52 -0
- package/client/csrTypes.ts +2 -2
- package/client/frameConfig.ts +2 -2
- package/client/index.ts +1 -0
- package/client/types.ts +0 -4
- package/common/fileUpload.ts +1 -1
- package/common/index.ts +1 -0
- package/common/isThenable.ts +12 -0
- package/constant/purify.ts +9 -10
- package/fetch/client/fetchClient.ts +1 -1
- package/fetch/client/httpClient.ts +9 -5
- package/fetch/fetchType/sliceFetch.type.ts +1 -1
- package/package.json +4 -7
- package/server/akanApp.ts +2 -0
- package/server/systemPageDocument.tsx +12 -12
- package/signal/endpointInfo.ts +2 -1
- package/signal/slice.ts +1 -1
- package/signal/sliceInfo.ts +2 -1
- package/store/action.ts +18 -2
- package/store/baseSt.ts +2 -2
- package/store/state.ts +9 -1
- package/store/storeInstance.ts +2 -0
- package/test/sampleOf.ts +3 -3
- package/test/signalTestRuntime.ts +0 -2
- package/types/base/baseEnv.d.ts +0 -2
- package/types/client/cn.d.ts +12 -0
- package/types/client/index.d.ts +1 -0
- package/types/client/types.d.ts +0 -3
- package/types/common/fileUpload.d.ts +1 -1
- package/types/common/index.d.ts +1 -0
- package/types/common/isThenable.d.ts +9 -0
- package/types/constant/purify.d.ts +3 -2
- package/types/fetch/fetchType/sliceFetch.type.d.ts +1 -1
- package/types/signal/endpointInfo.d.ts +2 -2
- package/types/signal/slice.d.ts +1 -1
- package/types/signal/sliceInfo.d.ts +2 -2
- package/types/store/action.d.ts +1 -1
- package/types/store/state.d.ts +4 -2
- package/types/test/sampleOf.d.ts +2 -2
- package/types/ui/Badge.d.ts +6 -0
- package/types/ui/Button.d.ts +26 -6
- package/types/ui/Load/Units.d.ts +3 -1
- package/types/ui/RecentTime.d.ts +1 -1
- package/types/ui/Signal/Collapse.d.ts +12 -0
- package/types/ui/Signal/style.d.ts +5 -6
- package/types/ui/Switch.d.ts +14 -0
- package/types/ui/Tooltip.d.ts +11 -0
- package/types/ui/UiOverride/Provider.d.ts +6 -5
- package/types/ui/UiOverride/context.d.ts +22 -1
- package/types/ui/UiOverride/index.d.ts +2 -1
- package/types/ui/UiOverride/override.d.ts +7 -6
- package/types/ui/UiOverride/useUiRecipe.d.ts +8 -0
- package/types/ui/index.d.ts +5 -1
- package/types/ui/recipe/badgeRecipe.d.ts +13 -0
- package/types/ui/recipe/buttonRecipe.d.ts +15 -0
- package/types/ui/recipe/factory.d.ts +24 -0
- package/types/ui/recipe/index.d.ts +18 -0
- package/types/ui/recipe/inputRecipe.d.ts +13 -0
- package/types/ui/recipe.d.ts +1 -0
- package/ui/Badge.tsx +17 -0
- package/ui/BottomSheet.tsx +8 -10
- package/ui/Button.tsx +158 -48
- package/ui/Clipboard.tsx +3 -3
- package/ui/Constant/Doc.tsx +108 -65
- package/ui/Constant/Mermaid.tsx +9 -9
- package/ui/Data/CardList.tsx +3 -3
- package/ui/Data/Dashboard.tsx +22 -13
- package/ui/Data/Insight.tsx +6 -6
- package/ui/Data/Item.tsx +49 -34
- package/ui/Data/ListContainer.tsx +11 -7
- package/ui/Data/Pagination.tsx +2 -2
- package/ui/DatePicker.tsx +5 -5
- package/ui/Dialog/Content.tsx +2 -2
- package/ui/Dialog/Modal.tsx +12 -8
- package/ui/Dialog/Provider.tsx +2 -2
- package/ui/DragAction.tsx +2 -2
- package/ui/DraggableList.tsx +10 -5
- package/ui/Dropdown.tsx +33 -26
- package/ui/Empty.tsx +5 -5
- package/ui/Field.tsx +82 -70
- package/ui/Image.tsx +2 -2
- package/ui/Input.tsx +62 -60
- package/ui/Layout/BottomInset.tsx +2 -2
- package/ui/Layout/BottomTab.tsx +5 -5
- package/ui/Layout/Header.tsx +3 -3
- package/ui/Layout/LeftSider.tsx +6 -5
- package/ui/Layout/Navbar.tsx +2 -12
- package/ui/Layout/RightSider.tsx +10 -8
- package/ui/Layout/Sider.tsx +5 -4
- package/ui/Layout/Template.tsx +2 -2
- package/ui/Layout/TopInset.tsx +2 -2
- package/ui/Layout/Unit.tsx +2 -2
- package/ui/Layout/View.tsx +2 -2
- package/ui/Layout/Zone.tsx +2 -2
- package/ui/Link/Back.tsx +2 -2
- package/ui/Link/Close.tsx +2 -2
- package/ui/Link/CsrLink.tsx +6 -4
- package/ui/Link/Lang.tsx +2 -2
- package/ui/Link/SsrLink.tsx +6 -5
- package/ui/Load/Page.tsx +1 -1
- package/ui/Load/Pagination.tsx +2 -2
- package/ui/Load/Units.tsx +19 -4
- package/ui/Load/View.tsx +2 -2
- package/ui/Loading/Area.tsx +4 -2
- package/ui/Loading/Button.tsx +2 -2
- package/ui/Loading/Input.tsx +2 -2
- package/ui/Loading/ProgressBar.tsx +2 -2
- package/ui/Loading/Skeleton.tsx +6 -6
- package/ui/Loading/Spin.tsx +2 -2
- package/ui/Menu.tsx +29 -27
- package/ui/Model/Edit.tsx +3 -3
- package/ui/Model/EditModal.tsx +5 -5
- package/ui/Model/EditWrapper.tsx +2 -2
- package/ui/Model/New.tsx +3 -3
- package/ui/Model/NewWrapper_Client.tsx +2 -2
- package/ui/Model/Remove.tsx +5 -4
- package/ui/Model/RemoveWrapper.tsx +2 -2
- package/ui/Model/SureToRemove.tsx +7 -6
- package/ui/Model/View.tsx +2 -2
- package/ui/Model/ViewEditModal.tsx +20 -21
- package/ui/Model/ViewWrapper.tsx +2 -2
- package/ui/More.tsx +2 -2
- package/ui/ObjectId.tsx +9 -3
- package/ui/Pagination.tsx +14 -11
- package/ui/Popconfirm.tsx +20 -11
- package/ui/Radio.tsx +4 -4
- package/ui/RecentTime.tsx +30 -83
- package/ui/Refresh.tsx +1 -1
- package/ui/ScreenNavigator.tsx +2 -2
- package/ui/Select.tsx +21 -23
- package/ui/Signal/Arg.tsx +21 -18
- package/ui/Signal/Collapse.tsx +29 -0
- package/ui/Signal/Doc.tsx +27 -22
- package/ui/Signal/Listener.tsx +3 -3
- package/ui/Signal/Message.tsx +31 -17
- package/ui/Signal/Object.tsx +13 -8
- package/ui/Signal/PubSub.tsx +26 -16
- package/ui/Signal/Request.tsx +3 -2
- package/ui/Signal/Response.tsx +5 -4
- package/ui/Signal/RestApi.tsx +27 -17
- package/ui/Signal/style.ts +16 -16
- package/ui/Switch.tsx +62 -0
- package/ui/System/CSR.tsx +40 -35
- package/ui/System/Client.tsx +2 -2
- package/ui/System/DevModeToggle.tsx +10 -12
- package/ui/System/Messages.tsx +14 -16
- package/ui/System/Reconnect.tsx +7 -7
- package/ui/System/SSR.tsx +25 -21
- package/ui/System/SelectLanguage.tsx +24 -28
- package/ui/System/ThemeToggle.tsx +38 -45
- package/ui/Tab/Menu.tsx +20 -18
- package/ui/Tab/Panel.tsx +3 -3
- package/ui/Tab/Provider.tsx +2 -2
- package/ui/Table.tsx +11 -8
- package/ui/ToggleSelect.tsx +14 -18
- package/ui/Tooltip.tsx +55 -0
- package/ui/UiOverride/Provider.tsx +10 -6
- package/ui/UiOverride/context.ts +22 -2
- package/ui/UiOverride/index.ts +8 -1
- package/ui/UiOverride/override.ts +7 -6
- package/ui/UiOverride/useUiRecipe.ts +15 -0
- package/ui/Unauthorized.tsx +5 -5
- package/ui/index.ts +16 -0
- package/ui/recipe/badgeRecipe.ts +23 -0
- package/ui/recipe/buttonRecipe.ts +32 -0
- package/ui/recipe/factory.ts +32 -0
- package/ui/recipe/index.ts +18 -0
- package/ui/recipe/inputRecipe.ts +13 -0
- package/ui/styles.css +150 -21
package/base/baseEnv.ts
CHANGED
|
@@ -13,8 +13,6 @@ export interface BaseEnv {
|
|
|
13
13
|
appName: string;
|
|
14
14
|
environment: Environment;
|
|
15
15
|
operationMode: "local" | "edge" | "cloud" | "module";
|
|
16
|
-
tunnelUsername: string;
|
|
17
|
-
tunnelPassword: string;
|
|
18
16
|
databaseMode?: DatabaseMode;
|
|
19
17
|
}
|
|
20
18
|
export type BackendEnv = BaseEnv & {
|
|
@@ -87,16 +85,12 @@ export const getEnv = (): ClientEnv => {
|
|
|
87
85
|
const environment = (process.env.AKAN_PUBLIC_ENV ?? "debug") as BaseEnv["environment"];
|
|
88
86
|
const operationMode = (process.env.AKAN_PUBLIC_OPERATION_MODE ??
|
|
89
87
|
(environment === "local" ? "local" : "cloud")) as BaseEnv["operationMode"];
|
|
90
|
-
const tunnelUsername = process.env.SSH_TUNNEL_USERNAME ?? "root";
|
|
91
|
-
const tunnelPassword = process.env.SSH_TUNNEL_PASSWORD ?? repoName;
|
|
92
88
|
const baseEnv: BaseEnv = {
|
|
93
89
|
repoName,
|
|
94
90
|
serveDomain,
|
|
95
91
|
appName,
|
|
96
92
|
environment,
|
|
97
93
|
operationMode,
|
|
98
|
-
tunnelUsername,
|
|
99
|
-
tunnelPassword,
|
|
100
94
|
databaseMode: process.env.AKAN_DATABASE_MODE as DatabaseMode | undefined,
|
|
101
95
|
} as const;
|
|
102
96
|
const side = typeof window === "undefined" ? "server" : "client";
|
package/client/cn.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type ClassNameValue, extendTailwindMerge } from "tailwind-merge";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Akan's semantic color tokens beyond tailwind-merge's built-in Tailwind palette.
|
|
5
|
+
* Registering them under `theme.color` teaches tailwind-merge that `bg-*`/`text-*`/
|
|
6
|
+
* `border-*`/… built from these names belong to the same conflict group, so
|
|
7
|
+
* `cn("bg-primary", "bg-open")` correctly resolves to `"bg-open"` instead of keeping both.
|
|
8
|
+
*/
|
|
9
|
+
export const colorTokens = [
|
|
10
|
+
"background",
|
|
11
|
+
"foreground",
|
|
12
|
+
"primary",
|
|
13
|
+
"primary-foreground",
|
|
14
|
+
"secondary",
|
|
15
|
+
"secondary-foreground",
|
|
16
|
+
"accent",
|
|
17
|
+
"accent-foreground",
|
|
18
|
+
"muted",
|
|
19
|
+
"muted-foreground",
|
|
20
|
+
"destructive",
|
|
21
|
+
"destructive-foreground",
|
|
22
|
+
"card",
|
|
23
|
+
"card-foreground",
|
|
24
|
+
"popover",
|
|
25
|
+
"popover-foreground",
|
|
26
|
+
"info",
|
|
27
|
+
"info-foreground",
|
|
28
|
+
"success",
|
|
29
|
+
"success-foreground",
|
|
30
|
+
"warning",
|
|
31
|
+
"warning-foreground",
|
|
32
|
+
"neutral",
|
|
33
|
+
"neutral-foreground",
|
|
34
|
+
"open",
|
|
35
|
+
"open-foreground",
|
|
36
|
+
"border",
|
|
37
|
+
"input",
|
|
38
|
+
"ring",
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const twMerge = extendTailwindMerge({
|
|
42
|
+
extend: {
|
|
43
|
+
theme: {
|
|
44
|
+
color: colorTokens,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/** The one class-combining function: joins strings/arrays/conditional parts (`cond && "x"`) and
|
|
50
|
+
* resolves Tailwind conflicts with a shared tailwind-merge instance that knows Akan's semantic
|
|
51
|
+
* color tokens. clsx-style object syntax (`{ x: cond }`) is not supported — write `cond && "x"`. */
|
|
52
|
+
export const cn = (...inputs: ClassNameValue[]) => twMerge(...inputs);
|
package/client/csrTypes.ts
CHANGED
|
@@ -270,8 +270,8 @@ export const defaultPageState: PageState = {
|
|
|
270
270
|
gesture: true,
|
|
271
271
|
cache: false,
|
|
272
272
|
ssr: "stream",
|
|
273
|
-
topSafeAreaColor: "var(--color-
|
|
274
|
-
bottomSafeAreaColor: "var(--color-
|
|
273
|
+
topSafeAreaColor: "var(--color-background, Canvas)",
|
|
274
|
+
bottomSafeAreaColor: "var(--color-background, Canvas)",
|
|
275
275
|
};
|
|
276
276
|
|
|
277
277
|
export interface Location {
|
package/client/frameConfig.ts
CHANGED
|
@@ -152,8 +152,8 @@ export function resolvePageState({
|
|
|
152
152
|
: (config.gesture ?? false),
|
|
153
153
|
cache: config.cache ?? false,
|
|
154
154
|
ssr: config.ssr ?? "stream",
|
|
155
|
-
topSafeAreaColor: config.topSafeAreaColor ?? "var(
|
|
156
|
-
bottomSafeAreaColor: config.bottomSafeAreaColor ?? "var(
|
|
155
|
+
topSafeAreaColor: config.topSafeAreaColor ?? "var(color-background, Canvas)",
|
|
156
|
+
bottomSafeAreaColor: config.bottomSafeAreaColor ?? "var(color-background, Canvas)",
|
|
157
157
|
};
|
|
158
158
|
}
|
|
159
159
|
|
package/client/index.ts
CHANGED
package/client/types.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import type { BaseInsight } from "akanjs/constant";
|
|
2
2
|
import type { FetchInitForm, SliceMeta } from "akanjs/fetch";
|
|
3
|
-
import { type ClassValue, clsx as clsxLib } from "clsx";
|
|
4
3
|
import type { ReactElement, ReactNode } from "react";
|
|
5
4
|
|
|
6
|
-
/** Composes class names with the shared clsx implementation. */
|
|
7
|
-
export const clsx = (...args: ClassValue[]) => clsxLib(...args);
|
|
8
|
-
|
|
9
5
|
export type ReactFontStyle = "normal" | "italic" | "oblique";
|
|
10
6
|
export type ReactFontDisplay = "auto" | "block" | "swap" | "fallback" | "optional";
|
|
11
7
|
export type ReactFontCategory = "monospace" | "sans-serif" | "serif" | "display";
|
package/common/fileUpload.ts
CHANGED
|
@@ -10,7 +10,7 @@ interface FileUploadSerializedSignal {
|
|
|
10
10
|
/** Framework-owned file-upload contract shared by client-safe packages. */
|
|
11
11
|
export const fileUploadContract = {
|
|
12
12
|
fields: { files: "files", metas: "metas", type: "type", parentId: "parentId" },
|
|
13
|
-
buildMetas: (fileList: FileList) =>
|
|
13
|
+
buildMetas: (fileList: FileList | File[]) =>
|
|
14
14
|
Array.from(fileList).map((f) => ({ lastModifiedAt: new Date(f.lastModified).toISOString(), size: f.size })),
|
|
15
15
|
} as const;
|
|
16
16
|
|
package/common/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export { isDayjs } from "./isDayjs";
|
|
|
15
15
|
export { isEmail } from "./isEmail";
|
|
16
16
|
export { isPhoneNumber } from "./isPhoneNumber";
|
|
17
17
|
export { isQueryEqual } from "./isQueryEqual";
|
|
18
|
+
export { isThenable } from "./isThenable";
|
|
18
19
|
export { isValidDate } from "./isValidDate";
|
|
19
20
|
export { decodeJwtPayload } from "./jwtDecode";
|
|
20
21
|
export { Logger, type LoggerSink, type LoggerSinkEntry, type LogLevel } from "./Logger";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a value is awaitable. Duck-typed rather than `instanceof Promise` so a thenable produced by a
|
|
3
|
+
* different realm or promise implementation still counts — a same-realm check silently misses those.
|
|
4
|
+
*
|
|
5
|
+
* `<Button>` uses this to decide whether a click handler is asynchronous: a handler that returns a
|
|
6
|
+
* thenable gets the pending/success states, a plain one renders as an ordinary button. That makes the
|
|
7
|
+
* distinction impossible to forget, unlike an explicit flag.
|
|
8
|
+
*/
|
|
9
|
+
export const isThenable = (value: unknown): value is PromiseLike<unknown> =>
|
|
10
|
+
!!value &&
|
|
11
|
+
(typeof value === "object" || typeof value === "function") &&
|
|
12
|
+
typeof Reflect.get(value, "then") === "function";
|
package/constant/purify.ts
CHANGED
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
ID,
|
|
11
11
|
PrimitiveRegistry,
|
|
12
12
|
type PrimitiveScalar,
|
|
13
|
-
type Upload,
|
|
14
13
|
} from "akanjs/base";
|
|
15
14
|
import { Logger } from "akanjs/common";
|
|
16
15
|
|
|
@@ -37,15 +36,15 @@ type PurifiedWithObjectToId<T, StateKeys extends keyof GetStateObject<T> = keyof
|
|
|
37
36
|
} & {
|
|
38
37
|
[K in StateKeys as null extends T[K] ? K : never]?: Purified<T[K]> | undefined;
|
|
39
38
|
};
|
|
40
|
-
export type PurifiedModel<T> = T extends
|
|
41
|
-
?
|
|
42
|
-
: T extends
|
|
43
|
-
?
|
|
44
|
-
: T extends
|
|
45
|
-
?
|
|
46
|
-
: T
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
export type PurifiedModel<T> = T extends (infer S)[]
|
|
40
|
+
? PurifiedModel<S>[]
|
|
41
|
+
: T extends string | number | boolean | Dayjs | File
|
|
42
|
+
? T
|
|
43
|
+
: T extends Map<infer K, infer V>
|
|
44
|
+
? Map<K, PurifiedModel<V>>
|
|
45
|
+
: PurifiedWithObjectToId<T>;
|
|
46
|
+
|
|
47
|
+
export type UploadableClientArg<T> = [T] extends [File[]] ? File[] | FileList : T;
|
|
49
48
|
|
|
50
49
|
export type PurifyFunc<Input, _DefaultInput = DefaultOf<Input>, _PurifiedInput = PurifiedModel<Input>> = (
|
|
51
50
|
self: _DefaultInput,
|
|
@@ -491,7 +491,7 @@ export class FetchClient {
|
|
|
491
491
|
this.#setHandlerFactory(
|
|
492
492
|
names.addModelFiles,
|
|
493
493
|
() =>
|
|
494
|
-
(async (fileList: FileList, parentId?: string, option?: FetchPolicy) => {
|
|
494
|
+
(async (fileList: FileList | File[], parentId?: string, option?: FetchPolicy) => {
|
|
495
495
|
const cap = resolveFileUploadCapability(this.serializedSignal);
|
|
496
496
|
const endpoint = cap ? this.serializedSignal[cap.refName]?.endpoint[cap.endpointKey] : undefined;
|
|
497
497
|
if (!cap || !endpoint)
|
|
@@ -125,6 +125,12 @@ export class HttpClient {
|
|
|
125
125
|
});
|
|
126
126
|
return `${paramedPath}${searchPath}`;
|
|
127
127
|
}
|
|
128
|
+
|
|
129
|
+
static #toUploadValues(argValue: unknown): (Blob | string)[] {
|
|
130
|
+
if (Array.isArray(argValue)) return argValue as (Blob | string)[];
|
|
131
|
+
if (typeof FileList !== "undefined" && argValue instanceof FileList) return Array.from(argValue);
|
|
132
|
+
return [argValue as Blob | string];
|
|
133
|
+
}
|
|
128
134
|
static makeBody(bodyArgs: SerializedArg[], uploadArgs: SerializedArg[], argMap: Map<string, unknown>) {
|
|
129
135
|
if (uploadArgs.length > 0) {
|
|
130
136
|
const formData = new FormData();
|
|
@@ -133,11 +139,9 @@ export class HttpClient {
|
|
|
133
139
|
if (arg.nullable && (argValue === null || argValue === undefined)) return;
|
|
134
140
|
if (!arg.nullable && (argValue === null || argValue === undefined))
|
|
135
141
|
throw new Error(`Argument ${arg.name} is required`);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
});
|
|
140
|
-
} else formData.append(arg.name, argValue as Blob | string);
|
|
142
|
+
HttpClient.#toUploadValues(argValue).forEach((value) => {
|
|
143
|
+
formData.append(arg.name, value);
|
|
144
|
+
});
|
|
141
145
|
});
|
|
142
146
|
bodyArgs.forEach((arg) => {
|
|
143
147
|
const argValue = argMap.get(arg.name);
|
|
@@ -152,7 +152,7 @@ type AppliedBaseSliceFetchType<
|
|
|
152
152
|
[K in `get${_CapitalizedRefName}Edit`]: (id: string, option?: FetchPolicy) => Promise<ServerEdit<RefName, Full>>;
|
|
153
153
|
} & {
|
|
154
154
|
[K in `add${_CapitalizedRefName}Files`]: (
|
|
155
|
-
fileList: FileList,
|
|
155
|
+
fileList: FileList | File[],
|
|
156
156
|
parentId?: string,
|
|
157
157
|
option?: FetchPolicy,
|
|
158
158
|
) => Promise<ProtoFile[]>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akanjs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-alpha.0",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -178,10 +178,11 @@
|
|
|
178
178
|
}
|
|
179
179
|
},
|
|
180
180
|
"dependencies": {
|
|
181
|
-
"clsx": "^2.1.1",
|
|
182
181
|
"dayjs": "^1.11.20",
|
|
183
182
|
"immer": "^11.1.8",
|
|
184
|
-
"sharp": "^0.34.5"
|
|
183
|
+
"sharp": "^0.34.5",
|
|
184
|
+
"tailwind-merge": "^3.3.1",
|
|
185
|
+
"tailwind-variants": "^3.3.0"
|
|
185
186
|
},
|
|
186
187
|
"peerDependencies": {
|
|
187
188
|
"@capacitor-community/contacts": "^7.2.0",
|
|
@@ -210,7 +211,6 @@
|
|
|
210
211
|
"chance": "^1.1.13",
|
|
211
212
|
"cordova-plugin-purchase": "^13.16.0",
|
|
212
213
|
"croner": "^10.0.1",
|
|
213
|
-
"daisyui": "5.5.23",
|
|
214
214
|
"firebase": "^12.13.0",
|
|
215
215
|
"ioredis": "^5.10.1",
|
|
216
216
|
"mermaid": "^11.15.0",
|
|
@@ -308,9 +308,6 @@
|
|
|
308
308
|
"croner": {
|
|
309
309
|
"optional": true
|
|
310
310
|
},
|
|
311
|
-
"daisyui": {
|
|
312
|
-
"optional": true
|
|
313
|
-
},
|
|
314
311
|
"firebase": {
|
|
315
312
|
"optional": true
|
|
316
313
|
},
|
package/server/akanApp.ts
CHANGED
|
@@ -39,19 +39,19 @@ export const SYSTEM_PAGE_STATUS_COPY = {
|
|
|
39
39
|
} as const;
|
|
40
40
|
|
|
41
41
|
const FALLBACK_STYLE = `
|
|
42
|
-
:root { color-scheme: dark; --akan-primary: #ff493b; --akan-secondary: #2b2e33; --akan-accent: #d1a23b; --akan-
|
|
43
|
-
body { margin: 0; min-height: 100vh; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: var(--akan-
|
|
42
|
+
:root { color-scheme: dark; --akan-primary: #ff493b; --akan-secondary: #2b2e33; --akan-accent: #d1a23b; --akan-foreground: #ffffff; --akan-background: #1a1a1a; --akan-base-200: #2a2a2a; --akan-error: #f02020; }
|
|
43
|
+
body { margin: 0; min-height: 100vh; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: var(--akan-background); color: var(--akan-foreground); }
|
|
44
44
|
a { color: inherit; }
|
|
45
|
-
.akan-system-page { min-height: 100vh; display: grid; place-items: center; padding: 32px 18px; box-sizing: border-box; background: radial-gradient(circle at top left, rgba(255, 73, 59, 0.16), transparent 34%), linear-gradient(135deg, var(--akan-
|
|
45
|
+
.akan-system-page { min-height: 100vh; display: grid; place-items: center; padding: 32px 18px; box-sizing: border-box; background: radial-gradient(circle at top left, rgba(255, 73, 59, 0.16), transparent 34%), linear-gradient(135deg, var(--akan-background), #111); }
|
|
46
46
|
.akan-system-card { width: min(720px, 100%); border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 32px; background: linear-gradient(145deg, rgba(42, 42, 42, 0.96), rgba(43, 46, 51, 0.78)); box-shadow: 0 24px 80px rgba(0, 0, 0, 0.36); padding: clamp(28px, 6vw, 56px); }
|
|
47
47
|
.akan-system-status { margin: 0 0 18px; font-weight: 800; font-size: clamp(4rem, 18vw, 8rem); line-height: 0.85; letter-spacing: -0.08em; color: var(--akan-primary); }
|
|
48
48
|
.akan-system-eyebrow { margin: 0 0 10px; color: var(--akan-accent); font-size: 0.78rem; font-weight: 800; letter-spacing: 0.18em; text-transform: uppercase; }
|
|
49
|
-
.akan-system-title { margin: 0; max-width: 12ch; font-size: clamp(2rem, 7vw, 4rem); line-height: 0.95; letter-spacing: -0.055em; color: var(--akan-
|
|
49
|
+
.akan-system-title { margin: 0; max-width: 12ch; font-size: clamp(2rem, 7vw, 4rem); line-height: 0.95; letter-spacing: -0.055em; color: var(--akan-foreground); }
|
|
50
50
|
.akan-system-description { margin: 22px 0 0; max-width: 56ch; color: rgba(255, 255, 255, 0.76); font-size: 1.05rem; line-height: 1.75; }
|
|
51
51
|
.akan-system-path { margin: 22px 0 0; overflow-wrap: anywhere; border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 16px; background: rgba(26, 26, 26, 0.72); padding: 12px 14px; color: rgba(255, 255, 255, 0.72); font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.88rem; }
|
|
52
52
|
.akan-system-actions { margin-top: 30px; display: flex; flex-wrap: wrap; gap: 12px; }
|
|
53
|
-
.akan-system-action { display: inline-flex; min-height: 44px; align-items: center; justify-content: center; border: 1px solid var(--akan-primary); border-radius: 999px; background: var(--akan-primary); box-shadow: 0 12px 34px rgba(255, 73, 59, 0.26); color: var(--akan-
|
|
54
|
-
.akan-system-secondary { border: 1px solid rgba(255, 255, 255, 0.12); background: var(--akan-secondary); color: var(--akan-
|
|
53
|
+
.akan-system-action { display: inline-flex; min-height: 44px; align-items: center; justify-content: center; border: 1px solid var(--akan-primary); border-radius: 999px; background: var(--akan-primary); box-shadow: 0 12px 34px rgba(255, 73, 59, 0.26); color: var(--akan-foreground); padding: 0 18px; font-weight: 800; text-decoration: none; }
|
|
54
|
+
.akan-system-secondary { border: 1px solid rgba(255, 255, 255, 0.12); background: var(--akan-secondary); color: var(--akan-foreground); }
|
|
55
55
|
.akan-system-details { margin-top: 28px; max-height: 260px; overflow: auto; border-radius: 18px; background: rgba(26, 26, 26, 0.82); border: 1px solid rgba(240, 32, 32, 0.22); padding: 16px; color: rgba(255, 255, 255, 0.78); font-size: 0.82rem; line-height: 1.55; white-space: pre-wrap; }
|
|
56
56
|
`;
|
|
57
57
|
|
|
@@ -73,9 +73,9 @@ export function createSystemPageDocument(options: SystemPageOptions): ReactNode
|
|
|
73
73
|
<style data-akan-system-page>{FALLBACK_STYLE}</style>
|
|
74
74
|
</head>
|
|
75
75
|
<body>
|
|
76
|
-
<main className="akan-system-page min-h-screen bg-
|
|
76
|
+
<main className="akan-system-page min-h-screen bg-background text-foreground">
|
|
77
77
|
<section
|
|
78
|
-
className="akan-system-card rounded-3xl border border-
|
|
78
|
+
className="akan-system-card rounded-3xl border border-foreground/10 bg-foreground/4 p-8 shadow-2xl backdrop-blur-xl"
|
|
79
79
|
aria-labelledby="akan-system-title"
|
|
80
80
|
>
|
|
81
81
|
<p className="akan-system-status text-primary">{copy.status}</p>
|
|
@@ -83,15 +83,15 @@ export function createSystemPageDocument(options: SystemPageOptions): ReactNode
|
|
|
83
83
|
<h1 id="akan-system-title" className="akan-system-title font-black">
|
|
84
84
|
{copy.title}
|
|
85
85
|
</h1>
|
|
86
|
-
<p className="akan-system-description text-
|
|
87
|
-
<p className="akan-system-path border border-
|
|
86
|
+
<p className="akan-system-description text-foreground/70">{copy.description}</p>
|
|
87
|
+
<p className="akan-system-path border border-foreground/10 bg-muted/50" aria-label="Requested path">
|
|
88
88
|
{options.pathname}
|
|
89
89
|
</p>
|
|
90
90
|
<div className="akan-system-actions">
|
|
91
|
-
<a className="akan-system-action
|
|
91
|
+
<a className="akan-system-action" href={options.homeHref}>
|
|
92
92
|
{copy.actionLabel}
|
|
93
93
|
</a>
|
|
94
|
-
<a className="akan-system-action akan-system-secondary
|
|
94
|
+
<a className="akan-system-action akan-system-secondary" href={options.pathname}>
|
|
95
95
|
Try again
|
|
96
96
|
</a>
|
|
97
97
|
</div>
|
package/signal/endpointInfo.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
ParamFieldType,
|
|
16
16
|
PlainTypeToFieldType,
|
|
17
17
|
PurifiedModel,
|
|
18
|
+
UploadableClientArg,
|
|
18
19
|
} from "akanjs/constant";
|
|
19
20
|
import type { ServiceModel } from "akanjs/service";
|
|
20
21
|
import type { InternalArgCls } from "./internalArg";
|
|
@@ -135,7 +136,7 @@ export class EndpointInfo<
|
|
|
135
136
|
Arg extends ConstantFieldTypeInput = PlainTypeToFieldType<ExplicitType>,
|
|
136
137
|
Optional extends boolean = false,
|
|
137
138
|
_ArgType = unknown extends ExplicitType ? FieldToValue<Arg> : ExplicitType,
|
|
138
|
-
_ClientArg = PurifiedModel<_ArgType
|
|
139
|
+
_ClientArg = UploadableClientArg<PurifiedModel<_ArgType>>,
|
|
139
140
|
_ServerArg = DocumentModel<_ArgType>,
|
|
140
141
|
>(name: ArgName, arg: Arg, option?: EndpointArgProps<Optional>) {
|
|
141
142
|
if (this.execFn) throw new Error("Query function is already set");
|
package/signal/slice.ts
CHANGED
package/signal/sliceInfo.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
PlainTypeToFieldType,
|
|
10
10
|
PurifiedModel,
|
|
11
11
|
QueryOf,
|
|
12
|
+
UploadableClientArg,
|
|
12
13
|
} from "akanjs/constant";
|
|
13
14
|
import type { FilterCls, FilterInstance } from "akanjs/document";
|
|
14
15
|
import type { ServiceModel } from "akanjs/service";
|
|
@@ -94,7 +95,7 @@ export class SliceInfo<
|
|
|
94
95
|
Arg extends ConstantFieldTypeInput = PlainTypeToFieldType<ExplicitType>,
|
|
95
96
|
Optional extends boolean = false,
|
|
96
97
|
_ArgType = unknown extends ExplicitType ? FieldToValue<Arg> : ExplicitType,
|
|
97
|
-
_ClientArg = PurifiedModel<_ArgType
|
|
98
|
+
_ClientArg = UploadableClientArg<PurifiedModel<_ArgType>>,
|
|
98
99
|
_ServerArg = DocumentModel<_ArgType>,
|
|
99
100
|
>(name: ArgName, arg: Arg, option?: EndpointArgProps<Optional>) {
|
|
100
101
|
if (this.execFn) throw new Error("Query function is already set");
|
package/store/action.ts
CHANGED
|
@@ -224,7 +224,7 @@ type FormSetter<
|
|
|
224
224
|
? K extends string
|
|
225
225
|
? SetterKey<"upload", K, RefName, _CapitalizedRefName>
|
|
226
226
|
: never
|
|
227
|
-
: never]: (fileList: FileList, idx?: number) => Promise<void>;
|
|
227
|
+
: never]: (fileList: FileList | File[], idx?: number) => Promise<void>;
|
|
228
228
|
} & {
|
|
229
229
|
[K in `writeOn${_CapitalizedRefName}`]: (path: string | (string | number)[], value: any) => void;
|
|
230
230
|
};
|
|
@@ -376,7 +376,11 @@ export const makeFormSetter = (refName: string, fetch: FetchProxy<any>) => {
|
|
|
376
376
|
: {}),
|
|
377
377
|
...(field.isClass && !!fileUploadRefName && ConstantRegistry.getRefName(field.modelRef) === fileUploadRefName
|
|
378
378
|
? {
|
|
379
|
-
[namesOfField.uploadFieldOnModel]: async function (
|
|
379
|
+
[namesOfField.uploadFieldOnModel]: async function (
|
|
380
|
+
this: SetGet,
|
|
381
|
+
fileList: FileList | File[],
|
|
382
|
+
index?: number,
|
|
383
|
+
) {
|
|
380
384
|
const form = (this.get() as { [key: string]: any })[names.modelForm] as { [key: string]: any };
|
|
381
385
|
if (!fileList.length) return;
|
|
382
386
|
const files = await (fetch[names.addModelFiles] as (...args: any) => Promise<ProtoFile[]>)(
|
|
@@ -476,6 +480,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
476
480
|
initModel: `init${className}`,
|
|
477
481
|
modelInitList: `${fieldName}InitList`,
|
|
478
482
|
modelInitAt: `${fieldName}InitAt`,
|
|
483
|
+
modelStaleAt: `${fieldName}StaleAt`,
|
|
479
484
|
refreshModel: `refresh${className}`,
|
|
480
485
|
selectModel: `select${className}`,
|
|
481
486
|
setPageOfModel: `setPageOf${className}`,
|
|
@@ -489,6 +494,14 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
489
494
|
queryArgsOfModel: `queryArgsOf${className}`,
|
|
490
495
|
sortOfModel: `sortOf${className}`,
|
|
491
496
|
};
|
|
497
|
+
const staleAtOfOtherSlices = (createdSliceName: string) => {
|
|
498
|
+
const staleAt = new Date();
|
|
499
|
+
return Object.fromEntries(
|
|
500
|
+
slices
|
|
501
|
+
.filter(({ sliceName }) => sliceName !== createdSliceName)
|
|
502
|
+
.map(({ sliceName }) => [sliceName.replace(names.model, names.modelStaleAt), staleAt]),
|
|
503
|
+
);
|
|
504
|
+
};
|
|
492
505
|
const baseAction = {
|
|
493
506
|
[names.createModelInForm]: async function (
|
|
494
507
|
this: SetGet,
|
|
@@ -527,6 +540,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
527
540
|
[namesOfSlice.modelInsight]: newModelInsight,
|
|
528
541
|
[names.modelViewAt]: new Date(),
|
|
529
542
|
[names.modelModal]: modal ?? null,
|
|
543
|
+
...staleAtOfOtherSlices(sliceName),
|
|
530
544
|
...(typeof path === "string" && path ? { [path]: model } : {}),
|
|
531
545
|
});
|
|
532
546
|
await onSuccess?.(model);
|
|
@@ -609,6 +623,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
609
623
|
[namesOfSlice.modelInsight]: newModelInsight,
|
|
610
624
|
[names.modelViewAt]: new Date(),
|
|
611
625
|
[names.modelModal]: modal ?? null,
|
|
626
|
+
...staleAtOfOtherSlices(sliceName),
|
|
612
627
|
...(typeof path === "string" && path ? { [path]: model } : {}),
|
|
613
628
|
});
|
|
614
629
|
await onSuccess?.(model);
|
|
@@ -845,6 +860,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
845
860
|
initModel: SliceName.replace(names.Model, names.initModel),
|
|
846
861
|
modelInitList: SliceName.replace(names.Model, names.modelInitList),
|
|
847
862
|
modelInitAt: SliceName.replace(names.Model, names.modelInitAt),
|
|
863
|
+
modelStaleAt: SliceName.replace(names.Model, names.modelStaleAt),
|
|
848
864
|
refreshModel: SliceName.replace(names.Model, names.refreshModel),
|
|
849
865
|
selectModel: SliceName.replace(names.Model, names.selectModel),
|
|
850
866
|
setPageOfModel: SliceName.replace(names.Model, names.setPageOfModel),
|
package/store/baseSt.ts
CHANGED
|
@@ -45,8 +45,8 @@ export class BaseStore extends store("base" as const, () => ({
|
|
|
45
45
|
gesture: true,
|
|
46
46
|
cache: false,
|
|
47
47
|
ssr: "stream",
|
|
48
|
-
topSafeAreaColor: "var(--color-
|
|
49
|
-
bottomSafeAreaColor: "var(--color-
|
|
48
|
+
topSafeAreaColor: "var(--color-background, Canvas)",
|
|
49
|
+
bottomSafeAreaColor: "var(--color-background, Canvas)",
|
|
50
50
|
} as PageState,
|
|
51
51
|
devMode: false,
|
|
52
52
|
deviceToken: "" as string,
|
package/store/state.ts
CHANGED
|
@@ -23,6 +23,7 @@ export type SliceStateKey =
|
|
|
23
23
|
| "modelListLoading"
|
|
24
24
|
| "modelInitList"
|
|
25
25
|
| "modelInitAt"
|
|
26
|
+
| "modelStaleAt"
|
|
26
27
|
| "modelSelection"
|
|
27
28
|
| "lastPageOfModel"
|
|
28
29
|
| "pageOfModel"
|
|
@@ -78,6 +79,8 @@ export type SliceState<
|
|
|
78
79
|
[K in `${RefName}InitList${_CapitalizedSuffix}`]: DataList<Light>;
|
|
79
80
|
} & {
|
|
80
81
|
[K in `${RefName}InitAt${_CapitalizedSuffix}`]: Date;
|
|
82
|
+
} & {
|
|
83
|
+
[K in `${RefName}StaleAt${_CapitalizedSuffix}`]: Date;
|
|
81
84
|
} & {
|
|
82
85
|
[K in `${RefName}Selection${_CapitalizedSuffix}`]: DataList<Light>;
|
|
83
86
|
} & {
|
|
@@ -112,7 +115,9 @@ type DefaultSliceStateFields<
|
|
|
112
115
|
| `${_RefName}InitList${StoreSliceSuffixCap<SlceCls, Suffix>}`
|
|
113
116
|
| `${_RefName}Selection${StoreSliceSuffixCap<SlceCls, Suffix>}`]: DataList<_Light>;
|
|
114
117
|
} & {
|
|
115
|
-
[Suffix in _Suffixes as
|
|
118
|
+
[Suffix in _Suffixes as
|
|
119
|
+
| `${_RefName}InitAt${StoreSliceSuffixCap<SlceCls, Suffix>}`
|
|
120
|
+
| `${_RefName}StaleAt${StoreSliceSuffixCap<SlceCls, Suffix>}`]: Date;
|
|
116
121
|
} & {
|
|
117
122
|
[Suffix in _Suffixes as `${_RefName}ListLoading${StoreSliceSuffixCap<SlceCls, Suffix>}`]: boolean;
|
|
118
123
|
} & {
|
|
@@ -182,6 +187,7 @@ export const createSliceState = (refName: string, slice: { [key: string]: Serial
|
|
|
182
187
|
modelListLoading: `${fieldName}ListLoading`,
|
|
183
188
|
modelInitList: `${fieldName}InitList`,
|
|
184
189
|
modelInitAt: `${fieldName}InitAt`,
|
|
190
|
+
modelStaleAt: `${fieldName}StaleAt`,
|
|
185
191
|
modelSelection: `${fieldName}Selection`,
|
|
186
192
|
modelInsight: `${fieldName}Insight`,
|
|
187
193
|
lastPageOfModel: `lastPageOf${className}`,
|
|
@@ -200,6 +206,7 @@ export const createSliceState = (refName: string, slice: { [key: string]: Serial
|
|
|
200
206
|
modelListLoading: sliceName.replace(names.model, names.modelListLoading),
|
|
201
207
|
modelInitList: sliceName.replace(names.model, names.modelInitList),
|
|
202
208
|
modelInitAt: sliceName.replace(names.model, names.modelInitAt),
|
|
209
|
+
modelStaleAt: sliceName.replace(names.model, names.modelStaleAt),
|
|
203
210
|
modelSelection: sliceName.replace(names.model, names.modelSelection),
|
|
204
211
|
modelInsight: sliceName.replace(names.model, names.modelInsight),
|
|
205
212
|
lastPageOfModel: SliceName.replace(names.Model, names.lastPageOfModel),
|
|
@@ -214,6 +221,7 @@ export const createSliceState = (refName: string, slice: { [key: string]: Serial
|
|
|
214
221
|
[namesOfSlice.modelListLoading]: true,
|
|
215
222
|
[namesOfSlice.modelInitList]: new DataList(),
|
|
216
223
|
[namesOfSlice.modelInitAt]: new Date(0),
|
|
224
|
+
[namesOfSlice.modelStaleAt]: new Date(0),
|
|
217
225
|
[namesOfSlice.modelSelection]: new DataList(),
|
|
218
226
|
[namesOfSlice.modelInsight]: new cnst.insight(),
|
|
219
227
|
[namesOfSlice.lastPageOfModel]: 1,
|
package/store/storeInstance.ts
CHANGED
|
@@ -263,6 +263,7 @@ export class StoreInstance {
|
|
|
263
263
|
modelListLoading: `${fieldName}ListLoading`,
|
|
264
264
|
modelInitList: `${fieldName}InitList`,
|
|
265
265
|
modelInitAt: `${fieldName}InitAt`,
|
|
266
|
+
modelStaleAt: `${fieldName}StaleAt`,
|
|
266
267
|
pageOfModel: `pageOf${className}`,
|
|
267
268
|
limitOfModel: `limitOf${className}`,
|
|
268
269
|
queryArgsOfModel: `queryArgsOf${className}`,
|
|
@@ -286,6 +287,7 @@ export class StoreInstance {
|
|
|
286
287
|
modelList: sliceName.replace(names.model, names.modelList),
|
|
287
288
|
modelListLoading: sliceName.replace(names.model, names.modelListLoading),
|
|
288
289
|
modelInitAt: SliceName.replace(names.Model, names.modelInitAt),
|
|
290
|
+
modelStaleAt: SliceName.replace(names.Model, names.modelStaleAt),
|
|
289
291
|
lastPageOfModel: SliceName.replace(names.Model, names.lastPageOfModel),
|
|
290
292
|
pageOfModel: SliceName.replace(names.Model, names.pageOfModel),
|
|
291
293
|
limitOfModel: SliceName.replace(names.Model, names.limitOfModel),
|
package/test/sampleOf.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
Upload,
|
|
11
11
|
} from "akanjs/base";
|
|
12
12
|
import { randomPick } from "akanjs/common";
|
|
13
|
-
import type { BaseObject, ConstantCls, ConstantField, DocumentModel, FieldPreset } from "akanjs/constant";
|
|
13
|
+
import type { BaseObject, ConstantCls, ConstantField, DocumentModel, FieldObject, FieldPreset } from "akanjs/constant";
|
|
14
14
|
|
|
15
15
|
import { sample } from "./sample";
|
|
16
16
|
|
|
@@ -57,8 +57,8 @@ const makeSample = (field: ConstantField): any => {
|
|
|
57
57
|
export type SampleOf<Model> = DocumentModel<{
|
|
58
58
|
[K in keyof Model as Model[K] extends BaseObject ? never : K]: NonNullable<Model[K]>;
|
|
59
59
|
}>;
|
|
60
|
-
export const sampleOf = <Model>(
|
|
61
|
-
modelRef: ConstantCls<Model>,
|
|
60
|
+
export const sampleOf = <Model, FieldObj extends FieldObject>(
|
|
61
|
+
modelRef: ConstantCls<Model, FieldObj>,
|
|
62
62
|
): DocumentModel<{ [K in keyof Model as Model[K] extends BaseObject ? never : K]: NonNullable<Model[K]> }> => {
|
|
63
63
|
|
|
64
64
|
return Object.fromEntries(
|
|
@@ -93,8 +93,6 @@ export const setupSignalTestTarget = async <Fetch = FetchProxy>(
|
|
|
93
93
|
appName: name,
|
|
94
94
|
environment: "testing",
|
|
95
95
|
operationMode: "local",
|
|
96
|
-
tunnelUsername: process.env.SSH_TUNNEL_USERNAME ?? "username",
|
|
97
|
-
tunnelPassword: process.env.SSH_TUNNEL_PASSWORD ?? process.env.AKAN_PUBLIC_REPO_NAME ?? "password",
|
|
98
96
|
};
|
|
99
97
|
TestServer.applyProcessEnv(env, resolvedOptions);
|
|
100
98
|
|
package/types/base/baseEnv.d.ts
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ClassNameValue } from "tailwind-merge";
|
|
2
|
+
/**
|
|
3
|
+
* Akan's semantic color tokens beyond tailwind-merge's built-in Tailwind palette.
|
|
4
|
+
* Registering them under `theme.color` teaches tailwind-merge that `bg-*`/`text-*`/
|
|
5
|
+
* `border-*`/… built from these names belong to the same conflict group, so
|
|
6
|
+
* `cn("bg-primary", "bg-open")` correctly resolves to `"bg-open"` instead of keeping both.
|
|
7
|
+
*/
|
|
8
|
+
export declare const colorTokens: string[];
|
|
9
|
+
/** The one class-combining function: joins strings/arrays/conditional parts (`cond && "x"`) and
|
|
10
|
+
* resolves Tailwind conflicts with a shared tailwind-merge instance that knows Akan's semantic
|
|
11
|
+
* color tokens. clsx-style object syntax (`{ x: cond }`) is not supported — write `cond && "x"`. */
|
|
12
|
+
export declare const cn: (...inputs: ClassNameValue[]) => string;
|
package/types/client/index.d.ts
CHANGED
package/types/client/types.d.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import type { BaseInsight } from "akanjs/constant";
|
|
2
2
|
import type { FetchInitForm, SliceMeta } from "akanjs/fetch";
|
|
3
|
-
import { type ClassValue } from "clsx";
|
|
4
3
|
import type { ReactElement, ReactNode } from "react";
|
|
5
|
-
/** Composes class names with the shared clsx implementation. */
|
|
6
|
-
export declare const clsx: (...args: ClassValue[]) => string;
|
|
7
4
|
export type ReactFontStyle = "normal" | "italic" | "oblique";
|
|
8
5
|
export type ReactFontDisplay = "auto" | "block" | "swap" | "fallback" | "optional";
|
|
9
6
|
export type ReactFontCategory = "monospace" | "sans-serif" | "serif" | "display";
|
|
@@ -13,7 +13,7 @@ export declare const fileUploadContract: {
|
|
|
13
13
|
readonly type: "type";
|
|
14
14
|
readonly parentId: "parentId";
|
|
15
15
|
};
|
|
16
|
-
readonly buildMetas: (fileList: FileList) => {
|
|
16
|
+
readonly buildMetas: (fileList: FileList | File[]) => {
|
|
17
17
|
lastModifiedAt: string;
|
|
18
18
|
size: number;
|
|
19
19
|
}[];
|
package/types/common/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export { isDayjs } from "./isDayjs.d.ts";
|
|
|
11
11
|
export { isEmail } from "./isEmail.d.ts";
|
|
12
12
|
export { isPhoneNumber } from "./isPhoneNumber.d.ts";
|
|
13
13
|
export { isQueryEqual } from "./isQueryEqual.d.ts";
|
|
14
|
+
export { isThenable } from "./isThenable.d.ts";
|
|
14
15
|
export { isValidDate } from "./isValidDate.d.ts";
|
|
15
16
|
export { decodeJwtPayload } from "./jwtDecode.d.ts";
|
|
16
17
|
export { Logger, type LoggerSink, type LoggerSinkEntry, type LogLevel } from "./Logger.d.ts";
|