@mieweb/ui 0.7.3-dev.1 → 0.7.3-dev.2

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/index.cjs CHANGED
@@ -1952,12 +1952,23 @@ function ComposerModelSelector({
1952
1952
  placeholder = "Model",
1953
1953
  anyLabel = "Any",
1954
1954
  emptyLabel = "No models",
1955
- ariaLabel = "Model"
1955
+ ariaLabel = "Model",
1956
+ effortOptions,
1957
+ effort = null,
1958
+ defaultEffort,
1959
+ onEffortChange,
1960
+ effortLabel = "Effort",
1961
+ effortHint,
1962
+ defaultBadgeLabel = "Default",
1963
+ backLabel = "Back"
1956
1964
  }) {
1957
1965
  const [open, setOpen] = React13__namespace.useState(false);
1966
+ const [view, setView] = React13__namespace.useState("models");
1967
+ const [effortHighlight, setEffortHighlight] = React13__namespace.useState(0);
1958
1968
  const [internalProviderFilter, setInternalProviderFilter] = React13__namespace.useState(null);
1959
1969
  const [highlightedIndex, setHighlightedIndex] = React13__namespace.useState(0);
1960
1970
  const listRef = React13__namespace.useRef(null);
1971
+ const effortListRef = React13__namespace.useRef(null);
1961
1972
  const menuId = React13__namespace.useId();
1962
1973
  const {
1963
1974
  anchorRef: triggerRef,
@@ -2021,6 +2032,10 @@ function ComposerModelSelector({
2021
2032
  (index) => `${menuId}-option-${index}`,
2022
2033
  [menuId]
2023
2034
  );
2035
+ const getEffortOptionId = React13__namespace.useCallback(
2036
+ (index) => `${menuId}-effort-option-${index}`,
2037
+ [menuId]
2038
+ );
2024
2039
  const activeOptionId = open && renderedModels[highlightedIndex] ? getOptionId(highlightedIndex) : void 0;
2025
2040
  const selectedRenderedIndex = React13__namespace.useMemo(
2026
2041
  () => selectedKey ? renderedModels.findIndex(
@@ -2053,6 +2068,48 @@ function ComposerModelSelector({
2053
2068
  },
2054
2069
  [close, onChange]
2055
2070
  );
2071
+ const efforts = React13__namespace.useMemo(() => effortOptions ?? [], [effortOptions]);
2072
+ const activeEffortOptionId = open && efforts[effortHighlight] ? getEffortOptionId(effortHighlight) : void 0;
2073
+ const selectedEffortOption = efforts.find(
2074
+ (option) => option.value === effort
2075
+ );
2076
+ const selectEffort = React13__namespace.useCallback(
2077
+ (value2) => {
2078
+ onEffortChange?.(value2);
2079
+ close();
2080
+ },
2081
+ [close, onEffortChange]
2082
+ );
2083
+ const handleEffortKeyDown = (event) => {
2084
+ if (efforts.length === 0) return;
2085
+ if (event.key === "ArrowDown") {
2086
+ event.preventDefault();
2087
+ setEffortHighlight((current) => (current + 1) % efforts.length);
2088
+ }
2089
+ if (event.key === "ArrowUp") {
2090
+ event.preventDefault();
2091
+ setEffortHighlight(
2092
+ (current) => (current - 1 + efforts.length) % efforts.length
2093
+ );
2094
+ }
2095
+ if (event.key === "Home") {
2096
+ event.preventDefault();
2097
+ setEffortHighlight(0);
2098
+ }
2099
+ if (event.key === "End") {
2100
+ event.preventDefault();
2101
+ setEffortHighlight(efforts.length - 1);
2102
+ }
2103
+ if (event.key === "ArrowLeft" || event.key === "Backspace") {
2104
+ event.preventDefault();
2105
+ setView("models");
2106
+ }
2107
+ if (event.key === "Enter" || event.key === " ") {
2108
+ event.preventDefault();
2109
+ const highlighted = efforts[effortHighlight];
2110
+ if (highlighted) selectEffort(highlighted.value);
2111
+ }
2112
+ };
2056
2113
  const outsideRefs = React13__namespace.useMemo(
2057
2114
  () => [triggerRef, menuRef],
2058
2115
  [triggerRef, menuRef]
@@ -2063,6 +2120,15 @@ function ComposerModelSelector({
2063
2120
  if (!open) return;
2064
2121
  setHighlightedIndex(selectedRenderedIndex >= 0 ? selectedRenderedIndex : 0);
2065
2122
  }, [activeProviderFilter, open, selectedRenderedIndex]);
2123
+ React13__namespace.useEffect(() => {
2124
+ if (!open) return;
2125
+ setView("models");
2126
+ }, [open]);
2127
+ React13__namespace.useEffect(() => {
2128
+ if (!open) return;
2129
+ const current = efforts.findIndex((option) => option.value === effort);
2130
+ setEffortHighlight(current >= 0 ? current : 0);
2131
+ }, [effort, efforts, open]);
2066
2132
  React13__namespace.useEffect(() => {
2067
2133
  if (!open) return;
2068
2134
  setHighlightedIndex(
@@ -2071,10 +2137,11 @@ function ComposerModelSelector({
2071
2137
  }, [open, renderedModels.length]);
2072
2138
  React13__namespace.useEffect(() => {
2073
2139
  if (!open) return;
2074
- requestAnimationFrame(
2075
- () => listRef.current?.focus({ preventScroll: true })
2076
- );
2077
- }, [open]);
2140
+ requestAnimationFrame(() => {
2141
+ const target = view === "effort" ? effortListRef.current : listRef.current;
2142
+ target?.focus({ preventScroll: true });
2143
+ });
2144
+ }, [open, view]);
2078
2145
  const handleTriggerKeyDown = (event) => {
2079
2146
  if (event.key === "ArrowDown" || event.key === "ArrowUp") {
2080
2147
  event.preventDefault();
@@ -2132,6 +2199,14 @@ function ComposerModelSelector({
2132
2199
  ),
2133
2200
  children: [
2134
2201
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: selectedOption?.label ?? selectedOption?.model ?? placeholder }),
2202
+ selectedEffortOption && /* @__PURE__ */ jsxRuntime.jsx(
2203
+ "span",
2204
+ {
2205
+ "data-slot": "composer-model-selector-trigger-effort",
2206
+ className: "text-muted-foreground min-w-0 truncate font-normal",
2207
+ children: selectedEffortOption.label
2208
+ }
2209
+ ),
2135
2210
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { "aria-hidden": "true", className: "h-3.5 w-3.5 shrink-0" })
2136
2211
  ]
2137
2212
  }
@@ -2148,90 +2223,190 @@ function ComposerModelSelector({
2148
2223
  "animate-in fade-in overflow-hidden duration-100"
2149
2224
  ),
2150
2225
  children: [
2151
- /* @__PURE__ */ jsxRuntime.jsx(
2152
- "div",
2153
- {
2154
- "data-slot": "composer-model-selector-provider-filter",
2155
- className: "border-border flex min-h-9 items-center gap-1 overflow-x-auto overflow-y-hidden border-b p-2",
2156
- children: [null, ...providers].map((provider) => {
2157
- const selected = activeProviderFilter === provider;
2158
- return /* @__PURE__ */ jsxRuntime.jsx(
2159
- "button",
2226
+ view === "models" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2227
+ /* @__PURE__ */ jsxRuntime.jsx(
2228
+ "div",
2229
+ {
2230
+ "data-slot": "composer-model-selector-provider-filter",
2231
+ className: "border-border flex min-h-9 items-center gap-1 overflow-x-auto overflow-y-hidden border-b p-2",
2232
+ children: [null, ...providers].map((provider) => {
2233
+ const selected = activeProviderFilter === provider;
2234
+ return /* @__PURE__ */ jsxRuntime.jsx(
2235
+ "button",
2236
+ {
2237
+ type: "button",
2238
+ "aria-pressed": selected,
2239
+ onMouseDown: (event) => event.preventDefault(),
2240
+ onClick: () => setProvider(provider),
2241
+ className: chunkS5RVSXDV_cjs.cn(
2242
+ "inline-flex h-6 shrink-0 items-center justify-center rounded-full px-2.5 text-xs font-medium whitespace-nowrap transition-colors",
2243
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none",
2244
+ selected ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted hover:text-foreground"
2245
+ ),
2246
+ children: provider === null ? anyLabel : providerLabels.get(provider) ?? provider
2247
+ },
2248
+ provider ?? "all-providers"
2249
+ );
2250
+ })
2251
+ }
2252
+ ),
2253
+ /* @__PURE__ */ jsxRuntime.jsx(
2254
+ "div",
2255
+ {
2256
+ ref: listRef,
2257
+ id: menuId,
2258
+ role: "listbox",
2259
+ "aria-label": ariaLabel,
2260
+ "aria-activedescendant": activeOptionId,
2261
+ tabIndex: -1,
2262
+ onKeyDown: handleMenuKeyDown,
2263
+ className: "min-h-0 flex-1 overflow-y-auto p-1",
2264
+ children: groupedModels.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-muted-foreground px-3 py-4 text-center text-sm", children: emptyLabel }) : groupedRenderedModels.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(
2265
+ "div",
2160
2266
  {
2161
- type: "button",
2162
- "aria-pressed": selected,
2163
- onMouseDown: (event) => event.preventDefault(),
2164
- onClick: () => setProvider(provider),
2165
- className: chunkS5RVSXDV_cjs.cn(
2166
- "inline-flex h-6 shrink-0 items-center justify-center rounded-full px-2.5 text-xs font-medium whitespace-nowrap transition-colors",
2167
- "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none",
2168
- selected ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted hover:text-foreground"
2169
- ),
2170
- children: provider === null ? anyLabel : providerLabels.get(provider) ?? provider
2267
+ role: "group",
2268
+ "aria-label": providerLabels.get(group.provider) ?? group.provider,
2269
+ children: [
2270
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-muted-foreground px-3 py-1.5 text-xs font-semibold uppercase", children: providerLabels.get(group.provider) ?? group.provider }),
2271
+ group.options.map((renderedModel) => {
2272
+ const { index, key, option: model } = renderedModel;
2273
+ const selected = optionKey(model) === selectedKey;
2274
+ const highlighted = index === highlightedIndex;
2275
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2276
+ "button",
2277
+ {
2278
+ id: getOptionId(index),
2279
+ type: "button",
2280
+ role: "option",
2281
+ "aria-selected": selected,
2282
+ onMouseDown: (event) => event.preventDefault(),
2283
+ onClick: () => selectModel(model),
2284
+ onMouseEnter: () => setHighlightedIndex(index),
2285
+ className: chunkS5RVSXDV_cjs.cn(
2286
+ "flex w-full items-center gap-2 rounded-md px-3 py-2 text-start text-sm",
2287
+ "focus-visible:ring-ring transition-colors focus-visible:ring-2 focus-visible:outline-none",
2288
+ highlighted && "bg-muted",
2289
+ selected && "bg-primary/10 text-primary"
2290
+ ),
2291
+ children: [
2292
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: model.label ?? model.model }),
2293
+ selected && /* @__PURE__ */ jsxRuntime.jsx(
2294
+ lucideReact.Check,
2295
+ {
2296
+ "aria-hidden": "true",
2297
+ className: "text-primary h-4 w-4 shrink-0"
2298
+ }
2299
+ )
2300
+ ]
2301
+ },
2302
+ key
2303
+ );
2304
+ })
2305
+ ]
2171
2306
  },
2172
- provider ?? "all-providers"
2173
- );
2174
- })
2175
- }
2176
- ),
2177
- /* @__PURE__ */ jsxRuntime.jsx(
2178
- "div",
2179
- {
2180
- ref: listRef,
2181
- id: menuId,
2182
- role: "listbox",
2183
- "aria-label": ariaLabel,
2184
- "aria-activedescendant": activeOptionId,
2185
- tabIndex: -1,
2186
- onKeyDown: handleMenuKeyDown,
2187
- className: "min-h-0 flex-1 overflow-y-auto p-1",
2188
- children: groupedModels.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-muted-foreground px-3 py-4 text-center text-sm", children: emptyLabel }) : groupedRenderedModels.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(
2189
- "div",
2190
- {
2191
- role: "group",
2192
- "aria-label": providerLabels.get(group.provider) ?? group.provider,
2193
- children: [
2194
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-muted-foreground px-3 py-1.5 text-xs font-semibold uppercase", children: providerLabels.get(group.provider) ?? group.provider }),
2195
- group.options.map((renderedModel) => {
2196
- const { index, key, option: model } = renderedModel;
2197
- const selected = optionKey(model) === selectedKey;
2198
- const highlighted = index === highlightedIndex;
2199
- return /* @__PURE__ */ jsxRuntime.jsxs(
2200
- "button",
2201
- {
2202
- id: getOptionId(index),
2203
- type: "button",
2204
- role: "option",
2205
- "aria-selected": selected,
2206
- onMouseDown: (event) => event.preventDefault(),
2207
- onClick: () => selectModel(model),
2208
- onMouseEnter: () => setHighlightedIndex(index),
2209
- className: chunkS5RVSXDV_cjs.cn(
2210
- "flex w-full items-center gap-2 rounded-md px-3 py-2 text-start text-sm",
2211
- "focus-visible:ring-ring transition-colors focus-visible:ring-2 focus-visible:outline-none",
2212
- highlighted && "bg-muted",
2213
- selected && "bg-primary/10 text-primary"
2214
- ),
2215
- children: [
2216
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: model.label ?? model.model }),
2217
- selected && /* @__PURE__ */ jsxRuntime.jsx(
2218
- lucideReact.Check,
2219
- {
2220
- "aria-hidden": "true",
2221
- className: "text-primary h-4 w-4 shrink-0"
2222
- }
2223
- )
2224
- ]
2225
- },
2226
- key
2227
- );
2228
- })
2229
- ]
2230
- },
2231
- group.provider
2232
- ))
2233
- }
2234
- )
2307
+ group.provider
2308
+ ))
2309
+ }
2310
+ ),
2311
+ efforts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-border border-t p-1", children: /* @__PURE__ */ jsxRuntime.jsxs(
2312
+ "button",
2313
+ {
2314
+ type: "button",
2315
+ "data-slot": "composer-model-selector-effort-row",
2316
+ "aria-haspopup": "listbox",
2317
+ onMouseDown: (event) => event.preventDefault(),
2318
+ onClick: () => setView("effort"),
2319
+ className: chunkS5RVSXDV_cjs.cn(
2320
+ "flex w-full items-center gap-2 rounded-md px-3 py-2 text-start text-sm",
2321
+ "hover:bg-muted transition-colors",
2322
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none"
2323
+ ),
2324
+ children: [
2325
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: effortLabel }),
2326
+ selectedEffortOption && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground min-w-0 truncate", children: selectedEffortOption.label }),
2327
+ /* @__PURE__ */ jsxRuntime.jsx(
2328
+ lucideReact.ChevronRight,
2329
+ {
2330
+ "aria-hidden": "true",
2331
+ className: "text-muted-foreground h-4 w-4 shrink-0"
2332
+ }
2333
+ )
2334
+ ]
2335
+ }
2336
+ ) })
2337
+ ] }),
2338
+ view === "effort" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2339
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-border flex items-center gap-1 border-b p-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
2340
+ "button",
2341
+ {
2342
+ type: "button",
2343
+ "data-slot": "composer-model-selector-effort-back",
2344
+ "aria-label": backLabel,
2345
+ onMouseDown: (event) => event.preventDefault(),
2346
+ onClick: () => setView("models"),
2347
+ className: chunkS5RVSXDV_cjs.cn(
2348
+ "inline-flex h-6 shrink-0 items-center gap-1 rounded-full px-2 text-xs font-medium",
2349
+ "text-muted-foreground hover:bg-muted hover:text-foreground transition-colors",
2350
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none"
2351
+ ),
2352
+ children: [
2353
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
2354
+ effortLabel
2355
+ ]
2356
+ }
2357
+ ) }),
2358
+ effortHint && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground px-3 pt-2 text-xs", children: effortHint }),
2359
+ /* @__PURE__ */ jsxRuntime.jsx(
2360
+ "div",
2361
+ {
2362
+ ref: effortListRef,
2363
+ id: menuId,
2364
+ role: "listbox",
2365
+ "aria-label": effortLabel,
2366
+ "aria-activedescendant": activeEffortOptionId,
2367
+ tabIndex: -1,
2368
+ onKeyDown: handleEffortKeyDown,
2369
+ className: "min-h-0 flex-1 overflow-y-auto p-1",
2370
+ children: efforts.map((option, index) => {
2371
+ const selected = option.value === effort;
2372
+ const highlighted = index === effortHighlight;
2373
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2374
+ "button",
2375
+ {
2376
+ id: getEffortOptionId(index),
2377
+ type: "button",
2378
+ role: "option",
2379
+ "aria-selected": selected,
2380
+ onMouseDown: (event) => event.preventDefault(),
2381
+ onClick: () => selectEffort(option.value),
2382
+ onMouseEnter: () => setEffortHighlight(index),
2383
+ className: chunkS5RVSXDV_cjs.cn(
2384
+ "flex w-full items-center gap-2 rounded-md px-3 py-2 text-start text-sm",
2385
+ "focus-visible:ring-ring transition-colors focus-visible:ring-2 focus-visible:outline-none",
2386
+ highlighted && "bg-muted",
2387
+ selected && "bg-primary/10 text-primary"
2388
+ ),
2389
+ children: [
2390
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 flex-1", children: [
2391
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate", children: option.label }),
2392
+ option.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground block truncate text-xs", children: option.description })
2393
+ ] }),
2394
+ option.value === defaultEffort && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bg-muted text-muted-foreground shrink-0 rounded px-1.5 py-0.5 text-xs", children: defaultBadgeLabel }),
2395
+ selected && /* @__PURE__ */ jsxRuntime.jsx(
2396
+ lucideReact.Check,
2397
+ {
2398
+ "aria-hidden": "true",
2399
+ className: "text-primary h-4 w-4 shrink-0"
2400
+ }
2401
+ )
2402
+ ]
2403
+ },
2404
+ option.value
2405
+ );
2406
+ })
2407
+ }
2408
+ )
2409
+ ] })
2235
2410
  ]
2236
2411
  }
2237
2412
  ),