@marimo-team/frontend 0.14.18-dev24 → 0.14.18-dev26
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/dist/assets/{ConnectedDataExplorerComponent-Dbu2xXc5.js → ConnectedDataExplorerComponent-CPwF1ckF.js} +1 -1
- package/dist/assets/{ImageComparisonComponent-BkZIjGHA.js → ImageComparisonComponent-BuFzvNj5.js} +1 -1
- package/dist/assets/{VegaLite-Ca7AXGyA.js → VegaLite-DI-xPLgU.js} +1 -1
- package/dist/assets/{_baseEach-H5Qk1V2B.js → _baseEach-BvIVAeE-.js} +1 -1
- package/dist/assets/_baseMap-DrbRr9s_.js +1 -0
- package/dist/assets/{_baseUniq-UAmxGez2.js → _baseUniq-Cz-AR5wG.js} +1 -1
- package/dist/assets/{_createAggregator-H-t5qYSG.js → _createAggregator-fFJIgkzg.js} +1 -1
- package/dist/assets/{any-language-editor-DUt-pIdy.js → any-language-editor-yovSkXXH.js} +1 -1
- package/dist/assets/{architectureDiagram-SUXI7LT5-DWP05q8_.js → architectureDiagram-SUXI7LT5-vhSKxFkz.js} +1 -1
- package/dist/assets/{blockDiagram-6J76NXCF-DB57b3LI.js → blockDiagram-6J76NXCF-D8FkmW5U.js} +1 -1
- package/dist/assets/{c4Diagram-6F6E4RAY-ByeAGPmY.js → c4Diagram-6F6E4RAY-DMz49WYv.js} +1 -1
- package/dist/assets/channel-C5D8_wdC.js +1 -0
- package/dist/assets/{chunk-353BL4L5-ngKqumF3.js → chunk-353BL4L5-C_h8QREg.js} +1 -1
- package/dist/assets/{chunk-67H74DCK-DTbVgB4A.js → chunk-67H74DCK-vQz9ujXD.js} +1 -1
- package/dist/assets/{chunk-AACKK3MU-t9Kf_p_V.js → chunk-AACKK3MU-CUxeSfqv.js} +1 -1
- package/dist/assets/{chunk-BFAMUDN2-B0dcgdMs.js → chunk-BFAMUDN2-CLGtBusC.js} +1 -1
- package/dist/assets/{chunk-E2GYISFI-C7n5SQvq.js → chunk-E2GYISFI-DGILtsZz.js} +1 -1
- package/dist/assets/{chunk-OW32GOEJ-DEvBFIh8.js → chunk-OW32GOEJ-D-HRiiaD.js} +1 -1
- package/dist/assets/{chunk-SKB7J2MH-DX71TF_n.js → chunk-SKB7J2MH-xs1kEiQZ.js} +1 -1
- package/dist/assets/{chunk-SZ463SBG-CO9fYjVe.js → chunk-SZ463SBG-DKY2KPhj.js} +1 -1
- package/dist/assets/{circle-play-B-ZWQLkW.js → circle-play-ub1A5yYA.js} +1 -1
- package/dist/assets/classDiagram-M3E45YP4-Bzxtjbca.js +1 -0
- package/dist/assets/classDiagram-v2-YAWTLIQI-Bzxtjbca.js +1 -0
- package/dist/assets/clone-DngVHtKT.js +1 -0
- package/dist/assets/{compile-lv6gzALt.js → compile-Eo2s_HHz.js} +1 -1
- package/dist/assets/{dagre-JOIXM2OF-B7ZtMRIJ.js → dagre-JOIXM2OF-BNvsGquX.js} +1 -1
- package/dist/assets/{data-grid-overlay-editor-DTAq6v9P.js → data-grid-overlay-editor-Vk_PgvbS.js} +1 -1
- package/dist/assets/{diagram-5UYTHUR4-CboeMF3G.js → diagram-5UYTHUR4-6gGyo4aQ.js} +1 -1
- package/dist/assets/{diagram-VMROVX33-BcnQcTFp.js → diagram-VMROVX33-Cr9VHqxg.js} +1 -1
- package/dist/assets/{diagram-ZTM2IBQH-CaFT3B8g.js → diagram-ZTM2IBQH-BFP88P3p.js} +1 -1
- package/dist/assets/{edit-page-CvWZ8wVZ.js → edit-page-Vrbmp55N.js} +53 -53
- package/dist/assets/{erDiagram-3M52JZNH-BFPc1-ng.js → erDiagram-3M52JZNH-C0U8FCGV.js} +1 -1
- package/dist/assets/{flowDiagram-KYDEHFYC-CAZBj4fC.js → flowDiagram-KYDEHFYC-3GT8PdlC.js} +1 -1
- package/dist/assets/{ganttDiagram-EK5VF46D-CUxffTMj.js → ganttDiagram-EK5VF46D-CrQQiHRJ.js} +1 -1
- package/dist/assets/{gitGraphDiagram-GW3U2K7C-Tx-ZfecV.js → gitGraphDiagram-GW3U2K7C-BuMcEd-g.js} +1 -1
- package/dist/assets/{glide-data-editor-BP8l5q3f.js → glide-data-editor-BB_hULql.js} +11 -11
- package/dist/assets/{graph-Bf6eoL3M.js → graph-B67OAr_1.js} +1 -1
- package/dist/assets/{home-page--ixzxF-w.js → home-page-D4saUb6P.js} +1 -1
- package/dist/assets/index-B3Q6PaCG.css +1 -0
- package/dist/assets/{index-g-JZ5Z-_.js → index-BCsh52ZA.js} +1 -1
- package/dist/assets/{index-DJ1FNShi.js → index-BUmTew_7.js} +1 -1
- package/dist/assets/{index-CmJm6kj5.js → index-BbaurO1n.js} +1 -1
- package/dist/assets/{index-D1vinr8C.js → index-BcNCs1Ec.js} +1 -1
- package/dist/assets/{index-Cv01rAR1.js → index-BcOm_dWX.js} +1 -1
- package/dist/assets/{index-dvYU8Vev.js → index-Bg-oxR2t.js} +94 -94
- package/dist/assets/{index-DleA5-JR.js → index-CT6WEyVV.js} +1 -1
- package/dist/assets/{index-kaWF-HKt.js → index-CWnyv0Mb.js} +1 -1
- package/dist/assets/{index-BMLuYuQT.js → index-CYZYUN8a.js} +1 -1
- package/dist/assets/{index-DupjaVjo.js → index-CfQw831p.js} +1 -1
- package/dist/assets/{index-C4iPAevr.js → index-CydDDozP.js} +1 -1
- package/dist/assets/{index-q4AwmgtF.js → index-D-aN-b2W.js} +1 -1
- package/dist/assets/{index-Uh_QGNQm.js → index-DFBulMSA.js} +1 -1
- package/dist/assets/{index-U0KSor5u.js → index-DeKrdrEq.js} +1 -1
- package/dist/assets/{index-Q73xW9dd.js → index-DqZ9_rHU.js} +1 -1
- package/dist/assets/{index-mvfeSH0B.js → index-Dtw1TXyN.js} +1 -1
- package/dist/assets/{index-B39Q37Jh.js → index-FrFiygIp.js} +1 -1
- package/dist/assets/{index-DhU7edG1.js → index-IDJxuXSR.js} +1 -1
- package/dist/assets/{index-CM8rF_ge.js → index-LcqVorg9.js} +1 -1
- package/dist/assets/infoDiagram-LHK5PUON-Bc9fde05.js +2 -0
- package/dist/assets/{journeyDiagram-EWQZEKCU-DBorgqR8.js → journeyDiagram-EWQZEKCU-CQkZyIS5.js} +1 -1
- package/dist/assets/{kanban-definition-ZSS6B67P-rOei7rdW.js → kanban-definition-ZSS6B67P-CEV1b6rk.js} +1 -1
- package/dist/assets/{layout-D5U1vfFv.js → layout-DAmvOShj.js} +1 -1
- package/dist/assets/{linear-BPaO6rYC.js → linear-ktW_rVCS.js} +1 -1
- package/dist/assets/links-C8gYI3jG.js +18 -0
- package/dist/assets/{mermaid-DuiiiGkf.js → mermaid-Dn9IUXi8.js} +4 -4
- package/dist/assets/{min-GM8d8p3k.js → min-BmsA5VGH.js} +1 -1
- package/dist/assets/{mindmap-definition-6CBA2TL7-DFNMYbU5.js → mindmap-definition-6CBA2TL7-B0yAK13C.js} +1 -1
- package/dist/assets/{number-overlay-editor-CcD_5O9P.js → number-overlay-editor-DbvoJDEj.js} +1 -1
- package/dist/assets/{pieDiagram-NIOCPIFQ-BAnWQSTZ.js → pieDiagram-NIOCPIFQ-BaPxK1u0.js} +1 -1
- package/dist/assets/{quadrantDiagram-2OG54O6I-CLzghZ4d.js → quadrantDiagram-2OG54O6I-PGZoquB4.js} +1 -1
- package/dist/assets/{react-plotly-qIomJONw.js → react-plotly-CzEXKtwr.js} +1 -1
- package/dist/assets/{requirementDiagram-QOLK2EJ7-ClKsOuLQ.js → requirementDiagram-QOLK2EJ7-BdGXi15P.js} +1 -1
- package/dist/assets/{run-page-B9ntqQci.js → run-page-BnZ5haku.js} +1 -1
- package/dist/assets/{sankeyDiagram-4UZDY2LN-CMUyusMd.js → sankeyDiagram-4UZDY2LN-Dk80vg6t.js} +1 -1
- package/dist/assets/{sequenceDiagram-SKLFT4DO-BUL7OokF.js → sequenceDiagram-SKLFT4DO-CqLVyCT3.js} +1 -1
- package/dist/assets/{slides-component-DxNxYl9E.js → slides-component-D_39bH-j.js} +1 -1
- package/dist/assets/{sortBy-Bo672N53.js → sortBy-CI9MGbno.js} +1 -1
- package/dist/assets/{stateDiagram-MI5ZYTHO-CK5D03xc.js → stateDiagram-MI5ZYTHO-C3tpbdm0.js} +1 -1
- package/dist/assets/stateDiagram-v2-5AN5P6BG-BXclnbG1.js +1 -0
- package/dist/assets/{storage-DCGJ86_2.js → storage-CxOI7c0Z.js} +3 -3
- package/dist/assets/{terminal-C9ZYVCQk.js → terminal-9Nyd6Apx.js} +1 -1
- package/dist/assets/{time-DUBsogDP.js → time-Do4-nT0Q.js} +1 -1
- package/dist/assets/{timeline-definition-MYPXXCX6-DlOzuKHL.js → timeline-definition-MYPXXCX6-CtllVQGu.js} +1 -1
- package/dist/assets/{tracing-DkB9iogQ.js → tracing-BWy-UTCz.js} +2 -2
- package/dist/assets/{trash-CuNNSzF1.js → trash-BPgHERlA.js} +1 -1
- package/dist/assets/{treemap-75Q7IDZK-DUsN_Z4F.js → treemap-75Q7IDZK-BXkwu4nl.js} +1 -1
- package/dist/assets/{vega-component-DByprFwW.js → vega-component-COxYVQBE.js} +1 -1
- package/dist/assets/{xychartDiagram-H2YORKM3-BaUqYAb6.js → xychartDiagram-H2YORKM3-DfzCBqlv.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/components/ai/ai-model-dropdown.tsx +288 -0
- package/src/components/ai/ai-provider-icon.tsx +7 -4
- package/src/components/app-config/ai-config.tsx +100 -76
- package/src/components/app-config/app-config-button.tsx +10 -1
- package/src/components/app-config/constants.ts +0 -34
- package/src/components/app-config/incorrect-model-id.tsx +4 -2
- package/src/components/app-config/user-config-form.tsx +12 -5
- package/src/components/chat/chat-panel.tsx +12 -26
- package/src/components/slides/slides.css +0 -1
- package/src/core/ai/__tests__/model-registry.test.ts +357 -0
- package/src/{utils/ai → core/ai/ids}/__tests__/ids.test.ts +2 -1
- package/src/{utils/ai → core/ai/ids}/ids.ts +18 -10
- package/src/core/ai/model-registry.ts +164 -0
- package/src/core/cells/cells.ts +1 -1
- package/src/core/cells/effects.ts +1 -1
- package/src/plugins/layout/carousel/CarouselPlugin.tsx +0 -2
- package/src/utils/__tests__/multi-map.test.ts +295 -0
- package/src/utils/multi-map.ts +71 -0
- package/dist/assets/_baseMap-lEtQfieX.js +0 -1
- package/dist/assets/channel-CJdgPvjM.js +0 -1
- package/dist/assets/classDiagram-M3E45YP4-Tb8oQ03C.js +0 -1
- package/dist/assets/classDiagram-v2-YAWTLIQI-Tb8oQ03C.js +0 -1
- package/dist/assets/clone-DygFoMzB.js +0 -1
- package/dist/assets/index-BlxPam9h.css +0 -1
- package/dist/assets/infoDiagram-LHK5PUON-Cz497oaY.js +0 -2
- package/dist/assets/links-Cxxlu7np.js +0 -17
- package/dist/assets/stateDiagram-v2-5AN5P6BG-Deqw4NDh.js +0 -1
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
AiModel as AiModelType,
|
|
5
|
+
AiProvider,
|
|
6
|
+
Role,
|
|
7
|
+
} from "@marimo-team/llm-info";
|
|
8
|
+
import { models } from "@marimo-team/llm-info/models.json";
|
|
9
|
+
import { providers } from "@marimo-team/llm-info/providers.json";
|
|
10
|
+
import { MultiMap } from "@/utils/multi-map";
|
|
11
|
+
import { once } from "@/utils/once";
|
|
12
|
+
import type { ProviderId } from "./ids/ids";
|
|
13
|
+
import { AiModelId, type QualifiedModelId, type ShortModelId } from "./ids/ids";
|
|
14
|
+
|
|
15
|
+
export interface AiModel extends AiModelType {
|
|
16
|
+
roles: Role[];
|
|
17
|
+
providers: ProviderId[];
|
|
18
|
+
/** Whether this is a custom model. */
|
|
19
|
+
custom: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const getKnownModelMap = once((): ReadonlyMap<QualifiedModelId, AiModel> => {
|
|
23
|
+
const modelMap = new Map<QualifiedModelId, AiModel>();
|
|
24
|
+
for (const model of models) {
|
|
25
|
+
const modelId = model.model as ShortModelId;
|
|
26
|
+
const modelInfo: AiModel = {
|
|
27
|
+
...model,
|
|
28
|
+
roles: model.roles.map((role) => role as Role),
|
|
29
|
+
providers: model.providers as ProviderId[],
|
|
30
|
+
custom: false,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
for (const provider of modelInfo.providers) {
|
|
34
|
+
const qualifiedModelId: QualifiedModelId = `${provider}/${modelId}`;
|
|
35
|
+
modelMap.set(qualifiedModelId, modelInfo);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return modelMap;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const getProviderMap = once((): ReadonlyMap<ProviderId, AiProvider> => {
|
|
42
|
+
const providerMap = new Map<ProviderId, AiProvider>();
|
|
43
|
+
for (const provider of providers) {
|
|
44
|
+
providerMap.set(provider.id as ProviderId, provider);
|
|
45
|
+
}
|
|
46
|
+
return providerMap;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export class AiModelRegistry {
|
|
50
|
+
private modelsByProviderMap = new MultiMap<ProviderId, AiModel>();
|
|
51
|
+
private modelsMap: ReadonlyMap<QualifiedModelId, AiModel>;
|
|
52
|
+
private customModels: ReadonlySet<QualifiedModelId>;
|
|
53
|
+
private displayedModels: ReadonlySet<QualifiedModelId>;
|
|
54
|
+
|
|
55
|
+
private constructor(
|
|
56
|
+
customModels: QualifiedModelId[],
|
|
57
|
+
displayedModels: QualifiedModelId[],
|
|
58
|
+
) {
|
|
59
|
+
this.customModels = new Set(customModels);
|
|
60
|
+
this.displayedModels = new Set(displayedModels);
|
|
61
|
+
this.modelsMap = new Map<QualifiedModelId, AiModel>();
|
|
62
|
+
this.buildMaps();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static getProviderInfo(providerId: ProviderId) {
|
|
66
|
+
return getProviderMap().get(providerId);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param customModels - A list of custom models to use that are not from the default list.
|
|
71
|
+
* @param displayedModels - A list of models to display in the UI. If empty, all models will be displayed.
|
|
72
|
+
*
|
|
73
|
+
* Models should be in the format of `provider_id/short_model_id`.
|
|
74
|
+
*/
|
|
75
|
+
static create(opts: { customModels?: string[]; displayedModels?: string[] }) {
|
|
76
|
+
const { customModels = [], displayedModels = [] } = opts;
|
|
77
|
+
return new AiModelRegistry(
|
|
78
|
+
customModels.map((model) => AiModelId.parse(model).id),
|
|
79
|
+
displayedModels.map((model) => AiModelId.parse(model).id),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Builds the maps of models by provider and custom models.
|
|
85
|
+
* Custom models are added first as they are specified by the user, so we want to surface them first.
|
|
86
|
+
*/
|
|
87
|
+
private buildMaps() {
|
|
88
|
+
const displayedModels = this.displayedModels;
|
|
89
|
+
const hasDisplayedModels = displayedModels.size > 0;
|
|
90
|
+
const knownModelMap = getKnownModelMap();
|
|
91
|
+
let modelsMap = new Map<QualifiedModelId, AiModel>();
|
|
92
|
+
|
|
93
|
+
// Start with known models
|
|
94
|
+
if (hasDisplayedModels) {
|
|
95
|
+
for (const model of displayedModels) {
|
|
96
|
+
if (knownModelMap.has(model)) {
|
|
97
|
+
const knownModel = knownModelMap.get(model);
|
|
98
|
+
if (knownModel) {
|
|
99
|
+
modelsMap.set(model, knownModel);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
modelsMap = new Map(knownModelMap);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Add custom models
|
|
108
|
+
for (const model of this.customModels) {
|
|
109
|
+
// Skip custom models that are not displayed
|
|
110
|
+
if (hasDisplayedModels && !displayedModels.has(model)) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// If custom model conflicts with a known model, skip it
|
|
115
|
+
if (modelsMap.has(model)) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const modelId = AiModelId.parse(model);
|
|
120
|
+
const modelInfo: AiModel = {
|
|
121
|
+
name: modelId.id,
|
|
122
|
+
model: modelId.shortModelId,
|
|
123
|
+
description: "Custom model",
|
|
124
|
+
providers: [modelId.providerId],
|
|
125
|
+
roles: [],
|
|
126
|
+
thinking: false,
|
|
127
|
+
custom: true,
|
|
128
|
+
};
|
|
129
|
+
modelsMap.set(model, modelInfo);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Group by provider
|
|
133
|
+
for (const [qualifiedModelId, model] of modelsMap.entries()) {
|
|
134
|
+
const modelId = AiModelId.parse(qualifiedModelId);
|
|
135
|
+
this.modelsByProviderMap.add(modelId.providerId, model);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.modelsMap = modelsMap;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
getDisplayedModels() {
|
|
142
|
+
return this.displayedModels;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
getCustomModels() {
|
|
146
|
+
return this.customModels;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
getModelsByProvider(provider: ProviderId) {
|
|
150
|
+
return this.modelsByProviderMap.get(provider) || [];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
getGroupedModelsByProvider() {
|
|
154
|
+
return this.modelsByProviderMap;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
getModelsMap() {
|
|
158
|
+
return this.modelsMap;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
getModel(qualifiedModelId: QualifiedModelId) {
|
|
162
|
+
return this.modelsMap.get(qualifiedModelId);
|
|
163
|
+
}
|
|
164
|
+
}
|
package/src/core/cells/cells.ts
CHANGED
|
@@ -1658,7 +1658,7 @@ export const useCellHandle = (cellId: CellId) =>
|
|
|
1658
1658
|
export const getAllEditorViews = () => {
|
|
1659
1659
|
const { cellIds, cellHandles } = store.get(notebookAtom);
|
|
1660
1660
|
return cellIds.inOrderIds
|
|
1661
|
-
.map((cellId) => cellHandles[cellId]?.current?.
|
|
1661
|
+
.map((cellId) => cellHandles[cellId]?.current?.editorViewOrNull)
|
|
1662
1662
|
.filter(Boolean);
|
|
1663
1663
|
};
|
|
1664
1664
|
|
|
@@ -9,7 +9,7 @@ import { store } from "../state/jotai";
|
|
|
9
9
|
import type { CellId } from "./ids";
|
|
10
10
|
|
|
11
11
|
const debounceSyncCellIds = debounce((request: UpdateCellIdsRequest) => {
|
|
12
|
-
getRequestClient().syncCellIds(request);
|
|
12
|
+
return getRequestClient().syncCellIds(request);
|
|
13
13
|
}, 400);
|
|
14
14
|
|
|
15
15
|
export const CellEffects = {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import React, { type JSX } from "react";
|
|
4
|
-
import swiperCssKeyboard from "swiper/css/keyboard?inline";
|
|
5
4
|
import swiperCssNavigation from "swiper/css/navigation?inline";
|
|
6
5
|
import swiperCssPagination from "swiper/css/pagination?inline";
|
|
7
6
|
import swiperCssScrollbar from "swiper/css/scrollbar?inline";
|
|
@@ -30,7 +29,6 @@ export class CarouselPlugin implements IStatelessPlugin<Data> {
|
|
|
30
29
|
cssStyles = [
|
|
31
30
|
swiperCss,
|
|
32
31
|
swiperCssVirtual,
|
|
33
|
-
swiperCssKeyboard,
|
|
34
32
|
swiperCssNavigation,
|
|
35
33
|
swiperCssPagination,
|
|
36
34
|
swiperCssScrollbar,
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { MultiMap } from "../multi-map";
|
|
5
|
+
|
|
6
|
+
describe("MultiMap", () => {
|
|
7
|
+
let multiMap: MultiMap<string, number>;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
multiMap = new MultiMap<string, number>();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe("constructor", () => {
|
|
14
|
+
it("should create an empty MultiMap", () => {
|
|
15
|
+
expect(multiMap.size).toBe(0);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe("get", () => {
|
|
20
|
+
it("should return empty array for non-existent key", () => {
|
|
21
|
+
expect(multiMap.get("nonexistent")).toEqual([]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should return values for existing key", () => {
|
|
25
|
+
multiMap.add("key1", 1);
|
|
26
|
+
multiMap.add("key1", 2);
|
|
27
|
+
expect(multiMap.get("key1")).toEqual([1, 2]);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe("set", () => {
|
|
32
|
+
it("should set values for a key", () => {
|
|
33
|
+
multiMap.set("key1", [1, 2, 3]);
|
|
34
|
+
expect(multiMap.get("key1")).toEqual([1, 2, 3]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should overwrite existing values", () => {
|
|
38
|
+
multiMap.add("key1", 1);
|
|
39
|
+
multiMap.set("key1", [4, 5, 6]);
|
|
40
|
+
expect(multiMap.get("key1")).toEqual([4, 5, 6]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should set empty array", () => {
|
|
44
|
+
multiMap.set("key1", []);
|
|
45
|
+
expect(multiMap.get("key1")).toEqual([]);
|
|
46
|
+
expect(multiMap.has("key1")).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe("add", () => {
|
|
51
|
+
it("should add value to new key", () => {
|
|
52
|
+
multiMap.add("key1", 1);
|
|
53
|
+
expect(multiMap.get("key1")).toEqual([1]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should add value to existing key", () => {
|
|
57
|
+
multiMap.add("key1", 1);
|
|
58
|
+
multiMap.add("key1", 2);
|
|
59
|
+
expect(multiMap.get("key1")).toEqual([1, 2]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should handle multiple keys", () => {
|
|
63
|
+
multiMap.add("key1", 1);
|
|
64
|
+
multiMap.add("key2", 2);
|
|
65
|
+
multiMap.add("key1", 3);
|
|
66
|
+
expect(multiMap.get("key1")).toEqual([1, 3]);
|
|
67
|
+
expect(multiMap.get("key2")).toEqual([2]);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe("has", () => {
|
|
72
|
+
it("should return false for non-existent key", () => {
|
|
73
|
+
expect(multiMap.has("nonexistent")).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should return true for existing key", () => {
|
|
77
|
+
multiMap.add("key1", 1);
|
|
78
|
+
expect(multiMap.has("key1")).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("should return true for key with empty array", () => {
|
|
82
|
+
multiMap.set("key1", []);
|
|
83
|
+
expect(multiMap.has("key1")).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("delete", () => {
|
|
88
|
+
it("should return false for non-existent key", () => {
|
|
89
|
+
expect(multiMap.delete("nonexistent")).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should delete existing key and return true", () => {
|
|
93
|
+
multiMap.add("key1", 1);
|
|
94
|
+
expect(multiMap.delete("key1")).toBe(true);
|
|
95
|
+
expect(multiMap.has("key1")).toBe(false);
|
|
96
|
+
expect(multiMap.get("key1")).toEqual([]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should not affect other keys", () => {
|
|
100
|
+
multiMap.add("key1", 1);
|
|
101
|
+
multiMap.add("key2", 2);
|
|
102
|
+
multiMap.delete("key1");
|
|
103
|
+
expect(multiMap.has("key2")).toBe(true);
|
|
104
|
+
expect(multiMap.get("key2")).toEqual([2]);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("clear", () => {
|
|
109
|
+
it("should clear empty MultiMap", () => {
|
|
110
|
+
multiMap.clear();
|
|
111
|
+
expect(multiMap.size).toBe(0);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should clear all keys and values", () => {
|
|
115
|
+
multiMap.add("key1", 1);
|
|
116
|
+
multiMap.add("key2", 2);
|
|
117
|
+
multiMap.clear();
|
|
118
|
+
expect(multiMap.size).toBe(0);
|
|
119
|
+
expect(multiMap.has("key1")).toBe(false);
|
|
120
|
+
expect(multiMap.has("key2")).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe("keys", () => {
|
|
125
|
+
it("should return empty iterator for empty MultiMap", () => {
|
|
126
|
+
const keys = [...multiMap.keys()];
|
|
127
|
+
expect(keys).toEqual([]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("should return all keys", () => {
|
|
131
|
+
multiMap.add("key1", 1);
|
|
132
|
+
multiMap.add("key2", 2);
|
|
133
|
+
multiMap.add("key3", 3);
|
|
134
|
+
const keys = [...multiMap.keys()];
|
|
135
|
+
expect(keys.sort()).toEqual(["key1", "key2", "key3"]);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("values", () => {
|
|
140
|
+
it("should return empty iterator for empty MultiMap", () => {
|
|
141
|
+
const values = [...multiMap.values()];
|
|
142
|
+
expect(values).toEqual([]);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("should return all value arrays", () => {
|
|
146
|
+
multiMap.add("key1", 1);
|
|
147
|
+
multiMap.add("key1", 2);
|
|
148
|
+
multiMap.add("key2", 3);
|
|
149
|
+
const values = [...multiMap.values()];
|
|
150
|
+
expect(values).toHaveLength(2);
|
|
151
|
+
expect(values).toContainEqual([1, 2]);
|
|
152
|
+
expect(values).toContainEqual([3]);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe("entries", () => {
|
|
157
|
+
it("should return empty iterator for empty MultiMap", () => {
|
|
158
|
+
const entries = [...multiMap.entries()];
|
|
159
|
+
expect(entries).toEqual([]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("should return all key-value pairs", () => {
|
|
163
|
+
multiMap.add("key1", 1);
|
|
164
|
+
multiMap.add("key1", 2);
|
|
165
|
+
multiMap.add("key2", 3);
|
|
166
|
+
const entries = [...multiMap.entries()];
|
|
167
|
+
expect(entries).toHaveLength(2);
|
|
168
|
+
expect(entries).toContainEqual(["key1", [1, 2]]);
|
|
169
|
+
expect(entries).toContainEqual(["key2", [3]]);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("forEach", () => {
|
|
174
|
+
it("should not call callback for empty MultiMap", () => {
|
|
175
|
+
const callback = vi.fn();
|
|
176
|
+
multiMap.forEach(callback);
|
|
177
|
+
expect(callback).not.toHaveBeenCalled();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("should call callback for each key-value pair", () => {
|
|
181
|
+
multiMap.add("key1", 1);
|
|
182
|
+
multiMap.add("key1", 2);
|
|
183
|
+
multiMap.add("key2", 3);
|
|
184
|
+
|
|
185
|
+
const callback = vi.fn();
|
|
186
|
+
multiMap.forEach(callback);
|
|
187
|
+
|
|
188
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
189
|
+
expect(callback).toHaveBeenCalledWith([1, 2], "key1", expect.any(Map));
|
|
190
|
+
expect(callback).toHaveBeenCalledWith([3], "key2", expect.any(Map));
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe("flatValues", () => {
|
|
195
|
+
it("should return empty array for empty MultiMap", () => {
|
|
196
|
+
expect(multiMap.flatValues()).toEqual([]);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("should flatten all values into single array", () => {
|
|
200
|
+
multiMap.add("key1", 1);
|
|
201
|
+
multiMap.add("key1", 2);
|
|
202
|
+
multiMap.add("key2", 3);
|
|
203
|
+
multiMap.add("key3", 4);
|
|
204
|
+
multiMap.add("key3", 5);
|
|
205
|
+
|
|
206
|
+
const flattened = multiMap.flatValues();
|
|
207
|
+
expect(flattened.sort()).toEqual([1, 2, 3, 4, 5]);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("should handle empty arrays in values", () => {
|
|
211
|
+
multiMap.set("key1", []);
|
|
212
|
+
multiMap.add("key2", 1);
|
|
213
|
+
multiMap.add("key2", 2);
|
|
214
|
+
|
|
215
|
+
const flattened = multiMap.flatValues();
|
|
216
|
+
expect(flattened.sort()).toEqual([1, 2]);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe("size", () => {
|
|
221
|
+
it("should return 0 for empty MultiMap", () => {
|
|
222
|
+
expect(multiMap.size).toBe(0);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("should return number of keys", () => {
|
|
226
|
+
multiMap.add("key1", 1);
|
|
227
|
+
expect(multiMap.size).toBe(1);
|
|
228
|
+
|
|
229
|
+
multiMap.add("key1", 2);
|
|
230
|
+
expect(multiMap.size).toBe(1);
|
|
231
|
+
|
|
232
|
+
multiMap.add("key2", 3);
|
|
233
|
+
expect(multiMap.size).toBe(2);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("should update when keys are deleted", () => {
|
|
237
|
+
multiMap.add("key1", 1);
|
|
238
|
+
multiMap.add("key2", 2);
|
|
239
|
+
expect(multiMap.size).toBe(2);
|
|
240
|
+
|
|
241
|
+
multiMap.delete("key1");
|
|
242
|
+
expect(multiMap.size).toBe(1);
|
|
243
|
+
|
|
244
|
+
multiMap.clear();
|
|
245
|
+
expect(multiMap.size).toBe(0);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe("different key and value types", () => {
|
|
250
|
+
it("should work with number keys and string values", () => {
|
|
251
|
+
const numberKeyMap = new MultiMap<number, string>();
|
|
252
|
+
numberKeyMap.add(1, "one");
|
|
253
|
+
numberKeyMap.add(1, "uno");
|
|
254
|
+
numberKeyMap.add(2, "two");
|
|
255
|
+
|
|
256
|
+
expect(numberKeyMap.get(1)).toEqual(["one", "uno"]);
|
|
257
|
+
expect(numberKeyMap.get(2)).toEqual(["two"]);
|
|
258
|
+
expect(numberKeyMap.flatValues()).toEqual(["one", "uno", "two"]);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("should work with object keys and values", () => {
|
|
262
|
+
interface TestObj {
|
|
263
|
+
id: number;
|
|
264
|
+
name: string;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const objMap = new MultiMap<string, TestObj>();
|
|
268
|
+
const obj1 = { id: 1, name: "test1" };
|
|
269
|
+
const obj2 = { id: 2, name: "test2" };
|
|
270
|
+
|
|
271
|
+
objMap.add("group1", obj1);
|
|
272
|
+
objMap.add("group1", obj2);
|
|
273
|
+
|
|
274
|
+
expect(objMap.get("group1")).toEqual([obj1, obj2]);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
describe("edge cases", () => {
|
|
279
|
+
it("should handle undefined and null values", () => {
|
|
280
|
+
const nullMap = new MultiMap<string, null | undefined>();
|
|
281
|
+
nullMap.add("key1", null);
|
|
282
|
+
nullMap.add("key1", undefined);
|
|
283
|
+
|
|
284
|
+
expect(nullMap.get("key1")).toEqual([null, undefined]);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("should handle duplicate values", () => {
|
|
288
|
+
multiMap.add("key1", 1);
|
|
289
|
+
multiMap.add("key1", 1);
|
|
290
|
+
multiMap.add("key1", 1);
|
|
291
|
+
|
|
292
|
+
expect(multiMap.get("key1")).toEqual([1, 1, 1]);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MultiMap: a Map<K, V[]> with convenient helpers.
|
|
5
|
+
*/
|
|
6
|
+
export class MultiMap<K, V> {
|
|
7
|
+
private map = new Map<K, V[]>();
|
|
8
|
+
|
|
9
|
+
get(key: K): V[] {
|
|
10
|
+
return this.map.get(key) ?? [];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
set(key: K, values: V[]): void {
|
|
14
|
+
this.map.set(key, values);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
add(key: K, value: V): void {
|
|
18
|
+
if (this.map.has(key)) {
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20
|
+
this.map.get(key)!.push(value);
|
|
21
|
+
} else {
|
|
22
|
+
this.map.set(key, [value]);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
has(key: K): boolean {
|
|
27
|
+
return this.map.has(key);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
delete(key: K): boolean {
|
|
31
|
+
return this.map.delete(key);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
clear(): void {
|
|
35
|
+
this.map.clear();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
keys(): IterableIterator<K> {
|
|
39
|
+
return this.map.keys();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
values(): IterableIterator<V[]> {
|
|
43
|
+
return this.map.values();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
entries(): IterableIterator<[K, V[]]> {
|
|
47
|
+
return this.map.entries();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
forEach(callback: (values: V[], key: K, map: Map<K, V[]>) => void): void {
|
|
51
|
+
this.map.forEach(callback);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Flatten all values into a single array.
|
|
56
|
+
*/
|
|
57
|
+
flatValues(): V[] {
|
|
58
|
+
const result: V[] = [];
|
|
59
|
+
for (const arr of this.map.values()) {
|
|
60
|
+
result.push(...arr);
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Number of keys in the MultiMap.
|
|
67
|
+
*/
|
|
68
|
+
get size(): number {
|
|
69
|
+
return this.map.size;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as m}from"./_baseEach-H5Qk1V2B.js";import{x as s}from"./index-dvYU8Vev.js";function e(r,o){var a=-1,t=s(r)?Array(r.length):[];return m(r,function(n,f,i){t[++a]=o(n,f,i)}),t}export{e as b};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{U as s,C as o}from"./mermaid-DuiiiGkf.js";const n=(a,r)=>s.lang.round(o.parse(a)[r]);export{n as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as t,C as o}from"./chunk-SZ463SBG-CO9fYjVe.js";import{_ as e}from"./mermaid-DuiiiGkf.js";import"./transform-B8bpuzxV.js";import"./chunk-E2GYISFI-C7n5SQvq.js";import"./chunk-BFAMUDN2-B0dcgdMs.js";import"./chunk-SKB7J2MH-DX71TF_n.js";import"./index-dvYU8Vev.js";import"./step-BwsUM5iJ.js";import"./timer-BwIYMJWC.js";var i={parser:t,get db(){return new o},renderer:s,styles:a,init:e(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{i as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as t,C as o}from"./chunk-SZ463SBG-CO9fYjVe.js";import{_ as e}from"./mermaid-DuiiiGkf.js";import"./transform-B8bpuzxV.js";import"./chunk-E2GYISFI-C7n5SQvq.js";import"./chunk-BFAMUDN2-B0dcgdMs.js";import"./chunk-SKB7J2MH-DX71TF_n.js";import"./index-dvYU8Vev.js";import"./step-BwsUM5iJ.js";import"./timer-BwIYMJWC.js";var i={parser:t,get db(){return new o},renderer:s,styles:a,init:e(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{i as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as n}from"./_baseUniq-UAmxGez2.js";function o(r){return n(r,4)}export{o as c};
|