@ouraihub/aether-ui 0.1.0 → 0.1.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/package.json +3 -3
- package/src/chat/ApprovalCard.svelte +8 -2
- package/src/chat/ChatComposer.svelte +319 -0
- package/src/chat/ChatMessage.svelte +262 -0
- package/src/chat/ChatPanel.css +386 -0
- package/src/chat/ChatPanel.svelte +276 -1880
- package/src/chat/ChatPanelApprovals.svelte +28 -0
- package/src/chat/ChatPanelComposer.svelte +336 -0
- package/src/chat/ChatPanelDialogs.svelte +242 -0
- package/src/chat/ChatPanelHeader.svelte +108 -0
- package/src/chat/ChatPanelMessages.svelte +166 -0
- package/src/chat/ChatPanelMode.svelte +190 -0
- package/src/chat/ChatPanelState.svelte.ts +1 -0
- package/src/chat/ChatPanelToolbar.svelte +436 -0
- package/src/chat/ChatSurface.svelte +168 -0
- package/src/chat/ContextUsageStatus.svelte +97 -0
- package/src/chat/MessageItem.svelte +111 -214
- package/src/chat/ModelEffortPicker.svelte +326 -0
- package/src/chat/PickerEffort.svelte +71 -0
- package/src/chat/ReasoningControl.svelte +184 -0
- package/src/chat/ReasoningSlider.svelte +174 -0
- package/src/chat/SkillPicker.svelte +14 -6
- package/src/chat/UsageControl.svelte +223 -0
- package/src/chat/UsageDetails.svelte +44 -90
- package/src/chat/chat-attachments.svelte.ts +78 -0
- package/src/chat/chat-panel-history.svelte.ts +430 -0
- package/src/chat/chat-panel-models.svelte.ts +97 -0
- package/src/chat/chat-panel-props.ts +38 -0
- package/src/chat/context-status.ts +101 -0
- package/src/chat/icons/LICENSE-REMIX +186 -0
- package/src/chat/icons/LICENSE-SIMPLE-ICONS +30 -0
- package/src/chat/icons/PathIcon.svelte +8 -0
- package/src/chat/icons/effort-icons.ts +65 -0
- package/src/chat/icons/status-icons.ts +105 -0
- package/src/chat/index.ts +25 -1
- package/src/chat/model-effort.ts +177 -0
- package/src/chat/run-session-history.ts +56 -0
- package/src/chat/run-session-types.ts +69 -0
- package/src/chat/run-session.svelte.ts +73 -135
- package/src/chat/test-support/chat-fixture.ts +194 -0
- package/src/chat/test-support/conversation-fixtures.ts +32 -0
- package/src/chat/test-support/run-session-fixture.ts +112 -0
- package/src/dev/Demo.svelte +3 -1
- package/src/i18n/en-chat.d.ts +1 -0
- package/src/i18n/en-chat.js +201 -0
- package/src/i18n/en.ts +58 -180
- package/src/i18n/index.ts +7 -2
- package/src/i18n/zh-CN-chat.d.ts +1 -0
- package/src/i18n/zh-CN-chat.js +190 -0
- package/src/i18n/zh-CN.ts +8 -153
- package/src/logs/LogPanel.svelte +9 -3
- package/src/logs/LogSettings.svelte +3 -1
- package/src/mcp/McpClientEditor.svelte +204 -0
- package/src/mcp/McpClientSettings.svelte +57 -130
- package/src/mcp/McpServerSettings.svelte +47 -11
- package/src/mcp/input.ts +2 -1
- package/src/ports/contract.ts +11 -2
- package/src/ports/fake-optional.ts +81 -0
- package/src/ports/fake.ts +90 -86
- package/src/ports/search.ts +11 -2
- package/src/ports/test-support/fake-fixture.ts +63 -0
- package/src/providers/EndpointKeysDialog.svelte +27 -13
- package/src/providers/EndpointModels.svelte +311 -0
- package/src/providers/EndpointPanel.svelte +33 -258
- package/src/providers/ModelPickerDialog.svelte +59 -16
- package/src/providers/ModelPricing.svelte +125 -0
- package/src/providers/ModelSelect.svelte +22 -8
- package/src/providers/ModelSettingsDialog.svelte +61 -126
- package/src/providers/ProviderDialogs.svelte +120 -0
- package/src/providers/ProviderList.svelte +159 -0
- package/src/providers/ProvidersSettings.svelte +67 -207
- package/src/providers/RequestConfigDialog.svelte +40 -12
- package/src/providers/SecretInput.svelte +11 -3
- package/src/providers/draft.ts +39 -13
- package/src/providers/edit-session.ts +31 -7
- package/src/providers/reasoning.ts +18 -4
- package/src/providers/revision.svelte.ts +3 -1
- package/src/providers/test-support/providers-fixture.ts +120 -0
- package/src/search/SearchAdvancedTools.svelte +182 -0
- package/src/search/SearchProviderCard.svelte +195 -0
- package/src/search/SearchSettings.svelte +62 -265
- package/src/search/settings-flow.ts +8 -3
- package/src/skills/SkillAdd.svelte +51 -18
- package/src/skills/SkillAuthoring.svelte +69 -20
- package/src/skills/SkillEditor.svelte +13 -3
- package/src/skills/SkillPreviewDialog.svelte +19 -5
- package/src/skills/SkillsSettings.svelte +11 -4
- package/src/skills/test-support/skills-fixture.ts +139 -0
- package/src/tauri/index.ts +23 -7
- package/src/web/index.ts +90 -25
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
Remix Icon License v1.0
|
|
2
|
+
Version 1.0 – January 2026
|
|
3
|
+
Copyright (c) 2017–2026 Remix Design
|
|
4
|
+
|
|
5
|
+
This License governs the use of the Remix Icon library, including all icons,
|
|
6
|
+
SVG files, font files, and related assets (collectively, the "Icons").
|
|
7
|
+
|
|
8
|
+
1. Grant of License
|
|
9
|
+
|
|
10
|
+
Subject to the terms and conditions of this License, Remix Design grants you
|
|
11
|
+
a worldwide, royalty-free, non-exclusive license to use, copy, modify,
|
|
12
|
+
merge, and distribute the Icons, as permitted below.
|
|
13
|
+
|
|
14
|
+
2. Permitted Uses
|
|
15
|
+
|
|
16
|
+
You are permitted to:
|
|
17
|
+
|
|
18
|
+
2.1 Commercial and Non-Commercial Use
|
|
19
|
+
Use the Icons in personal, commercial, and client projects, including
|
|
20
|
+
but not limited to:
|
|
21
|
+
- Websites, web applications, and mobile applications
|
|
22
|
+
- Software products and SaaS platforms
|
|
23
|
+
- User interface designs, design systems, and UI kits
|
|
24
|
+
- Presentations, documentation, and marketing materials
|
|
25
|
+
- Physical or digital products where the Icons are decorative or
|
|
26
|
+
functional elements
|
|
27
|
+
|
|
28
|
+
2.2 Modification and Derivative Works
|
|
29
|
+
- Modify, adapt, or customize the Icons
|
|
30
|
+
- Change colors, sizes, stroke styles, or other visual attributes
|
|
31
|
+
to fit your project needs
|
|
32
|
+
|
|
33
|
+
2.3 Distribution as Part of a Larger Work
|
|
34
|
+
Include the Icons (modified or unmodified) as part of a larger product,
|
|
35
|
+
provided that:
|
|
36
|
+
- The Icons are functional or decorative components of the product, and
|
|
37
|
+
- The Icons are not marketed or sold as the primary value of the product
|
|
38
|
+
|
|
39
|
+
2.4 Attribution (Optional)
|
|
40
|
+
Attribution to Remix Icon is appreciated but not required.
|
|
41
|
+
Suggested attribution:
|
|
42
|
+
"Icons by Remix Icon (https://remixicon.com)"
|
|
43
|
+
|
|
44
|
+
3. Restrictions
|
|
45
|
+
|
|
46
|
+
3.1 Prohibited: Standalone Icon Sales
|
|
47
|
+
You may NOT:
|
|
48
|
+
- Sell, sublicense, or distribute the Icons as a standalone product
|
|
49
|
+
- Package the Icons as an independent icon pack, icon library, or
|
|
50
|
+
icon font for sale
|
|
51
|
+
- Offer the Icons as a downloadable asset collection where the Icons
|
|
52
|
+
themselves constitute the primary product value
|
|
53
|
+
|
|
54
|
+
Clarification:
|
|
55
|
+
A product is considered a prohibited standalone icon sale if:
|
|
56
|
+
- The Icons are the primary advertised feature, or
|
|
57
|
+
- Removing the Icons would eliminate the core value of the product
|
|
58
|
+
|
|
59
|
+
Examples:
|
|
60
|
+
- Prohibited: "Premium Icon Pack – 2,000+ Icons for $29"
|
|
61
|
+
- Prohibited: "Extended Remix Icon Library (Paid)"
|
|
62
|
+
- Permitted: Website or app templates that include Icons as UI elements
|
|
63
|
+
- Permitted: Design systems or UI kits where Icons are a minor component
|
|
64
|
+
|
|
65
|
+
3.2 Prohibited: Competing Icon Libraries
|
|
66
|
+
You may NOT use the Icons to create, distribute, or sell a competing
|
|
67
|
+
icon library or icon set, even if the Icons are modified with only
|
|
68
|
+
minor or superficial changes (such as color, size, stroke width,
|
|
69
|
+
corner radius, or layout).
|
|
70
|
+
|
|
71
|
+
3.3 Prohibited: Logo, Trademark, and Brand Identity Use
|
|
72
|
+
You may NOT use the Icons, whether modified or unmodified, as:
|
|
73
|
+
- A logo, trademark, or brand identifier
|
|
74
|
+
- The primary visual identity of a company, product, or service
|
|
75
|
+
|
|
76
|
+
This includes, but is not limited to:
|
|
77
|
+
- Creating a logo by applying simple modifications such as color changes,
|
|
78
|
+
rotation, combination, or layout
|
|
79
|
+
- Using an Icon as a brand mark, app icon, or identity symbol where the
|
|
80
|
+
Icon itself represents the brand
|
|
81
|
+
|
|
82
|
+
Rationale:
|
|
83
|
+
Remix Icon is designed for functional and informational use. Using the
|
|
84
|
+
Icons as brand identifiers may impair their recognizability and neutrality
|
|
85
|
+
in other functional contexts.
|
|
86
|
+
|
|
87
|
+
4. Brand Icons and Trademarks
|
|
88
|
+
|
|
89
|
+
Some Icons represent third-party brands or services.
|
|
90
|
+
|
|
91
|
+
4.1 Permitted Use of Brand Icons
|
|
92
|
+
You may use brand icons solely to represent or link to the corresponding
|
|
93
|
+
brand, product, or service.
|
|
94
|
+
Examples:
|
|
95
|
+
- Using a GitHub icon to link to a GitHub repository
|
|
96
|
+
- Displaying social media icons in a website footer
|
|
97
|
+
|
|
98
|
+
4.2 Prohibited Use of Brand Icons
|
|
99
|
+
You may NOT:
|
|
100
|
+
- Use brand icons as your own logo or trademark
|
|
101
|
+
- Modify brand icons to create a new brand identity
|
|
102
|
+
- Use brand icons in a way that implies endorsement or affiliation
|
|
103
|
+
by the brand owner
|
|
104
|
+
- Use brand icons for purposes unrelated to the represented brand
|
|
105
|
+
|
|
106
|
+
4.3 Trademark Responsibility
|
|
107
|
+
All brand icons are subject to the trademark rights of their respective
|
|
108
|
+
owners. This License does not grant any trademark rights.
|
|
109
|
+
Users are responsible for ensuring that their use of brand icons complies
|
|
110
|
+
with applicable trademark laws.
|
|
111
|
+
|
|
112
|
+
5. Copyright and License Notice
|
|
113
|
+
|
|
114
|
+
When distributing the complete Icon library or substantial portions thereof:
|
|
115
|
+
|
|
116
|
+
- You must retain existing copyright notices in source files
|
|
117
|
+
- You should include a copy of this License or a link to it
|
|
118
|
+
|
|
119
|
+
For individual use of Icons within projects (e.g., compiled apps, websites,
|
|
120
|
+
presentations), no copyright notice is required.
|
|
121
|
+
|
|
122
|
+
6. No Trademark Rights
|
|
123
|
+
|
|
124
|
+
This License does not grant permission to use:
|
|
125
|
+
|
|
126
|
+
- The "Remix Icon" name, logo, or trademarks
|
|
127
|
+
- Any trademarks represented by brand icons
|
|
128
|
+
|
|
129
|
+
You may refer to Remix Icon by name solely for attribution purposes.
|
|
130
|
+
|
|
131
|
+
7. Disclaimer of Warranty
|
|
132
|
+
|
|
133
|
+
THE ICONS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
134
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY,
|
|
135
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
|
|
136
|
+
|
|
137
|
+
8. Limitation of Liability
|
|
138
|
+
|
|
139
|
+
IN NO EVENT SHALL REMIX DESIGN BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
|
|
140
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING
|
|
141
|
+
FROM, OUT OF, OR IN CONNECTION WITH THE ICONS OR THEIR USE.
|
|
142
|
+
|
|
143
|
+
9. License Compatibility
|
|
144
|
+
|
|
145
|
+
You may integrate the Icons into projects under permissive licenses
|
|
146
|
+
(such as MIT, BSD, or Apache 2.0), provided that:
|
|
147
|
+
|
|
148
|
+
- The Icons themselves remain governed by this License
|
|
149
|
+
- The restrictions in Section 3 continue to apply
|
|
150
|
+
|
|
151
|
+
This License may be incompatible with licenses that require unrestricted
|
|
152
|
+
redistribution of assets as standalone products or mandatory application
|
|
153
|
+
of strong copyleft or share-alike terms.
|
|
154
|
+
|
|
155
|
+
10. Termination
|
|
156
|
+
|
|
157
|
+
Your rights under this License terminate automatically if you breach its
|
|
158
|
+
terms.
|
|
159
|
+
|
|
160
|
+
Upon termination:
|
|
161
|
+
|
|
162
|
+
- You must cease any distribution that violates this License
|
|
163
|
+
- You may continue to use the Icons in projects already distributed
|
|
164
|
+
prior to termination
|
|
165
|
+
- End users who received such projects are not affected
|
|
166
|
+
|
|
167
|
+
11. License Versioning
|
|
168
|
+
|
|
169
|
+
Remix Design may publish revised versions of this License.
|
|
170
|
+
|
|
171
|
+
You may choose to use the Icons under:
|
|
172
|
+
|
|
173
|
+
- The version of this License under which you received them, or
|
|
174
|
+
- Any later version published by Remix Design
|
|
175
|
+
|
|
176
|
+
Once chosen, that version may be used indefinitely.
|
|
177
|
+
|
|
178
|
+
How to Apply This License
|
|
179
|
+
|
|
180
|
+
Include the following notice in your documentation when appropriate:
|
|
181
|
+
|
|
182
|
+
"This project uses icons from Remix Icon (https://remixicon.com),
|
|
183
|
+
licensed under the Remix Icon License v1.0."
|
|
184
|
+
|
|
185
|
+
When distributing the complete Icon library or substantial portions of it,
|
|
186
|
+
include a copy of this License.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# CC0 1.0 Universal
|
|
2
|
+
|
|
3
|
+
## Statement of Purpose
|
|
4
|
+
|
|
5
|
+
The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an “owner”) of an original work of authorship and/or a database (each, a “Work”).
|
|
6
|
+
|
|
7
|
+
Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works (“Commons”) that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
|
|
8
|
+
|
|
9
|
+
For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the “Affirmer”), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
|
|
10
|
+
|
|
11
|
+
1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights (“Copyright and Related Rights”). Copyright and Related Rights include, but are not limited to, the following:
|
|
12
|
+
1. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
|
|
13
|
+
2. moral rights retained by the original author(s) and/or performer(s);
|
|
14
|
+
3. publicity and privacy rights pertaining to a person’s image or likeness depicted in a Work;
|
|
15
|
+
4. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(i), below;
|
|
16
|
+
5. rights protecting the extraction, dissemination, use and reuse of data in a Work;
|
|
17
|
+
6. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and
|
|
18
|
+
7. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
|
|
19
|
+
|
|
20
|
+
2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer’s Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the “Waiver”). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer’s heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer’s express Statement of Purpose.
|
|
21
|
+
|
|
22
|
+
3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer’s express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer’s Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the “License”). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer’s express Statement of Purpose.
|
|
23
|
+
|
|
24
|
+
4. Limitations and Disclaimers.
|
|
25
|
+
1. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
|
|
26
|
+
2. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
|
|
27
|
+
3. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person’s Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.
|
|
28
|
+
4. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
|
|
29
|
+
|
|
30
|
+
For more information, please see <https://creativecommons.org/publicdomain/zero/1.0>.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// A 24×24 single-path mark colored by `currentColor`, for marks absent from the icon library.
|
|
3
|
+
import type { SVGAttributes } from 'svelte/elements'
|
|
4
|
+
|
|
5
|
+
let { d, ...rest }: { d: string } & SVGAttributes<SVGSVGElement> = $props()
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<svg viewBox="0 0 24 24" aria-hidden="true" {...rest}><path fill="currentColor" {d} /></svg>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Effort marks as 24×24 path data, colored by `currentColor`; one mark per reasoning level.
|
|
2
|
+
// Remix Icon 4.9.0 (LICENSE-REMIX): RiZzz, RiLeaf, RiFlashlight, RiContrastDrop2, RiSparkling2,
|
|
3
|
+
// RiSparkling and RiFire, Line variants. Path data unmodified.
|
|
4
|
+
import type { ReasoningEffort } from '../../ports'
|
|
5
|
+
|
|
6
|
+
const paths: Partial<Record<ReasoningEffort, string>> = {
|
|
7
|
+
none: [
|
|
8
|
+
'M11 11V13L5.67257 19H11V21H3V19L8.32557 13H3V11H11ZM21 3V5L15.6726 11H21V13H13V11L18',
|
|
9
|
+
'.3256 5H13V3H21Z',
|
|
10
|
+
].join(''),
|
|
11
|
+
minimal: [
|
|
12
|
+
'M20.998 3V5C20.998 14.6274 15.6255 19 8.99805 19L5.24077 18.9999C5.0786 19.912 4.998',
|
|
13
|
+
'05 20.907 4.99805 22H2.99805C2.99805 20.6373 3.11376 19.3997 3.34381 18.2682C3.1133 ',
|
|
14
|
+
'16.9741 2.99805 15.2176 2.99805 13C2.99805 7.47715 7.4752 3 12.998 3C14.998 3 16.998',
|
|
15
|
+
' 4 20.998 3ZM12.998 5C8.57977 5 4.99805 8.58172 4.99805 13C4.99805 13.3624 5.00125 1',
|
|
16
|
+
'3.7111 5.00759 14.0459C6.26198 12.0684 8.09902 10.5048 10.5019 9.13176L11.4942 10.86',
|
|
17
|
+
'82C8.6393 12.4996 6.74554 14.3535 5.77329 16.9998L8.99805 17C15.0132 17 18.8692 13.0',
|
|
18
|
+
'269 18.9949 5.38766C17.6229 5.52113 16.3481 5.436 14.7754 5.20009C13.6243 5.02742 13',
|
|
19
|
+
'.3988 5 12.998 5Z',
|
|
20
|
+
].join(''),
|
|
21
|
+
low: ['M13 9H21L11 24V15H4L13 0V9ZM11 11V7.22063L7.53238 13H13V17.3944L17.263 11H11Z'].join(''),
|
|
22
|
+
medium: [
|
|
23
|
+
'M12 3.09735L7.05025 8.04709C4.31658 10.7808 4.31658 15.2129 7.05025 17.9466C9.78392 ',
|
|
24
|
+
'20.6803 14.2161 20.6803 16.9497 17.9466C19.6834 15.2129 19.6834 10.7808 16.9497 8.04',
|
|
25
|
+
'71L12 3.09735ZM12 0.268921L18.364 6.63288C21.8787 10.1476 21.8787 15.8461 18.364 19.',
|
|
26
|
+
'3608C14.8492 22.8755 9.15076 22.8755 5.63604 19.3608C2.12132 15.8461 2.12132 10.1476',
|
|
27
|
+
' 5.63604 6.63288L12 0.268921ZM7 12.9968H17C17 15.7583 14.7614 17.9968 12 17.9968C9.2',
|
|
28
|
+
'3858 17.9968 7 15.7583 7 12.9968Z',
|
|
29
|
+
].join(''),
|
|
30
|
+
high: [
|
|
31
|
+
'M17.0007 1.20825 18.3195 3.68108 20.7923 4.99992 18.3195 6.31876 17.0007 8.79159 15.',
|
|
32
|
+
'6818 6.31876 13.209 4.99992 15.6818 3.68108 17.0007 1.20825ZM10.6673 9.33325 15.6673',
|
|
33
|
+
' 11.9999 10.6673 14.6666 8.00065 19.6666 5.33398 14.6666.333984 11.9999 5.33398 9.33',
|
|
34
|
+
'325 8.00065 4.33325 10.6673 9.33325ZM11.4173 11.9999 9.18905 10.8115 8.00065 8.58325',
|
|
35
|
+
' 6.81224 10.8115 4.58398 11.9999 6.81224 13.1883 8.00065 15.4166 9.18905 13.1883 11.',
|
|
36
|
+
'4173 11.9999ZM19.6673 16.3333 18.0007 13.2083 16.334 16.3333 13.209 17.9999 16.334 1',
|
|
37
|
+
'9.6666 18.0007 22.7916 19.6673 19.6666 22.7923 17.9999 19.6673 16.3333Z',
|
|
38
|
+
].join(''),
|
|
39
|
+
xhigh: [
|
|
40
|
+
'M14 4.4375C15.3462 4.4375 16.4375 3.34619 16.4375 2H17.5625C17.5625 3.34619 18.6538 ',
|
|
41
|
+
'4.4375 20 4.4375V5.5625C18.6538 5.5625 17.5625 6.65381 17.5625 8H16.4375C16.4375 6.6',
|
|
42
|
+
'5381 15.3462 5.5625 14 5.5625V4.4375ZM1 11C4.31371 11 7 8.31371 7 5H9C9 8.31371 11.6',
|
|
43
|
+
'863 11 15 11V13C11.6863 13 9 15.6863 9 19H7C7 15.6863 4.31371 13 1 13V11ZM4.87601 12',
|
|
44
|
+
'C6.18717 12.7276 7.27243 13.8128 8 15.124 8.72757 13.8128 9.81283 12.7276 11.124 12 ',
|
|
45
|
+
'9.81283 11.2724 8.72757 10.1872 8 8.87601 7.27243 10.1872 6.18717 11.2724 4.87601 12',
|
|
46
|
+
'ZM17.25 14C17.25 15.7949 15.7949 17.25 14 17.25V18.75C15.7949 18.75 17.25 20.2051 17',
|
|
47
|
+
'.25 22H18.75C18.75 20.2051 20.2051 18.75 22 18.75V17.25C20.2051 17.25 18.75 15.7949 ',
|
|
48
|
+
'18.75 14H17.25Z',
|
|
49
|
+
].join(''),
|
|
50
|
+
max: [
|
|
51
|
+
'M12 23C16.1421 23 19.5 19.6421 19.5 15.5C19.5 14.6345 19.2697 13.8032 19 13.0296C17.',
|
|
52
|
+
'3333 14.6765 16.0667 15.5 15.2 15.5C19.1954 8.5 17 5.5 11 1.5C11.5 6.49951 8.20403 8',
|
|
53
|
+
'.77375 6.86179 10.0366C5.40786 11.4045 4.5 13.3462 4.5 15.5C4.5 19.6421 7.85786 23 1',
|
|
54
|
+
'2 23ZM12.7094 5.23498C15.9511 7.98528 15.9666 10.1223 13.463 14.5086C12.702 15.8419 ',
|
|
55
|
+
'13.6648 17.5 15.2 17.5C15.8884 17.5 16.5841 17.2992 17.3189 16.9051C16.6979 19.262 1',
|
|
56
|
+
'4.5519 21 12 21C8.96243 21 6.5 18.5376 6.5 15.5C6.5 13.9608 7.13279 12.5276 8.23225 ',
|
|
57
|
+
'11.4932C8.35826 11.3747 8.99749 10.8081 9.02477 10.7836C9.44862 10.4021 9.7978 10.06',
|
|
58
|
+
'63 10.1429 9.69677C11.3733 8.37932 12.2571 6.91631 12.7094 5.23498Z',
|
|
59
|
+
].join(''),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** The mark for `effort`; `auto` and `default` share the medium drop. */
|
|
63
|
+
export function effortPath(effort: ReasoningEffort): string {
|
|
64
|
+
return paths[effort] ?? (paths.medium as string)
|
|
65
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Status marks as 24×24 path data, colored by `currentColor`.
|
|
2
|
+
// Apple and Linux: Simple Icons (CC0, LICENSE-SIMPLE-ICONS). Windows: original geometry.
|
|
3
|
+
// Wand: Remix Icon 4.9.0 RiMagicLine (LICENSE-REMIX). Brand marks stay their owners' marks.
|
|
4
|
+
|
|
5
|
+
export const windowsPath = [
|
|
6
|
+
'M2 5.2 10.2 4v7.2H2zm9.5-1.4L22 2.3v8.9H11.5zM2 12.6h8.2v7.2L2 18.7zm9.5 0H22v8.9L11',
|
|
7
|
+
'.5 20z',
|
|
8
|
+
].join('')
|
|
9
|
+
|
|
10
|
+
export const applePath = [
|
|
11
|
+
'M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.',
|
|
12
|
+
'675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.',
|
|
13
|
+
'935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.6',
|
|
14
|
+
'36-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.5',
|
|
15
|
+
'59-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.',
|
|
16
|
+
'012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3',
|
|
17
|
+
'.714 1.338.104 2.715-.688 3.559-1.701',
|
|
18
|
+
].join('')
|
|
19
|
+
|
|
20
|
+
export const linuxPath = [
|
|
21
|
+
'M12.504 0c-.155 0-.315.008-.48.021-4.226.333-3.105 4.807-3.17 6.298-.076 1.092-.3 1.',
|
|
22
|
+
'953-1.05 3.02-.885 1.051-2.127 2.75-2.716 4.521-.278.832-.41 1.684-.287 2.489a.424.4',
|
|
23
|
+
'24 0 00-.11.135c-.26.268-.45.6-.663.839-.199.199-.485.267-.797.4-.313.136-.658.269-.',
|
|
24
|
+
'864.68-.09.189-.136.394-.132.602 0 .199.027.4.055.536.058.399.116.728.04.97-.249.68-',
|
|
25
|
+
'.28 1.145-.106 1.484.174.334.535.47.94.601.81.2 1.91.135 2.774.6.926.466 1.866.67 2.',
|
|
26
|
+
'616.47.526-.116.97-.464 1.208-.946.587-.003 1.23-.269 2.26-.334.699-.058 1.574.267 2',
|
|
27
|
+
'.577.2.025.134.063.198.114.333l.003.003c.391.778 1.113 1.132 1.884 1.071.771-.06 1.5',
|
|
28
|
+
'92-.536 2.257-1.306.631-.765 1.683-1.084 2.378-1.503.348-.199.629-.469.649-.853.023-',
|
|
29
|
+
'.4-.2-.811-.714-1.376v-.097l-.003-.003c-.17-.2-.25-.535-.338-.926-.085-.401-.182-.78',
|
|
30
|
+
'6-.492-1.046h-.003c-.059-.054-.123-.067-.188-.135a.357.357 0 00-.19-.064c.431-1.278.',
|
|
31
|
+
'264-2.55-.173-3.694-.533-1.41-1.465-2.638-2.175-3.483-.796-1.005-1.576-1.957-1.56-3.',
|
|
32
|
+
'368.026-2.152.236-6.133-3.544-6.139zm.529 3.405h.013c.213 0 .396.062.584.198.19.135.',
|
|
33
|
+
'33.332.438.533.105.259.158.459.166.724 0-.02.006-.04.006-.06v.105a.086.086 0 01-.004',
|
|
34
|
+
'-.021l-.004-.024a1.807 1.807 0 01-.15.706.953.953 0 01-.213.335.71.71 0 00-.088-.042',
|
|
35
|
+
'c-.104-.045-.198-.064-.284-.133a1.312 1.312 0 00-.22-.066c.05-.06.146-.133.183-.198.',
|
|
36
|
+
'053-.128.082-.264.088-.402v-.02a1.21 1.21 0 00-.061-.4c-.045-.134-.101-.2-.183-.333-',
|
|
37
|
+
'.084-.066-.167-.132-.267-.132h-.016c-.093 0-.176.03-.262.132a.8.8 0 00-.205.334 1.18',
|
|
38
|
+
' 1.18 0 00-.09.4v.019c.002.089.008.179.02.267-.193-.067-.438-.135-.607-.202a1.635 1.',
|
|
39
|
+
'635 0 01-.018-.2v-.02a1.772 1.772 0 01.15-.768c.082-.22.232-.406.43-.533a.985.985 0 ',
|
|
40
|
+
'01.594-.2zm-2.962.059h.036c.142 0 .27.048.399.135.146.129.264.288.344.465.09.199.14.',
|
|
41
|
+
'4.153.667v.004c.007.134.006.2-.002.266v.08c-.03.007-.056.018-.083.024-.152.055-.274.',
|
|
42
|
+
'135-.393.2.012-.09.013-.18.003-.267v-.015c-.012-.133-.04-.2-.082-.333a.613.613 0 00-',
|
|
43
|
+
'.166-.267.248.248 0 00-.183-.064h-.021c-.071.006-.13.04-.186.132a.552.552 0 00-.12.2',
|
|
44
|
+
'7.944.944 0 00-.023.33v.015c.012.135.037.2.08.334.046.134.098.2.166.268.01.009.02.01',
|
|
45
|
+
'8.034.024-.07.057-.117.07-.176.136a.304.304 0 01-.131.068 2.62 2.62 0 01-.275-.402 1',
|
|
46
|
+
'.772 1.772 0 01-.155-.667 1.759 1.759 0 01.08-.668 1.43 1.43 0 01.283-.535c.128-.133',
|
|
47
|
+
'.26-.2.418-.2zm1.37 1.706c.332 0 .733.065 1.216.399.293.2.523.269 1.052.468h.003c.25',
|
|
48
|
+
'5.136.405.266.478.399v-.131a.571.571 0 01.016.47c-.123.31-.516.643-1.063.842v.002c-.',
|
|
49
|
+
'268.135-.501.333-.775.465-.276.135-.588.292-1.012.267a1.139 1.139 0 01-.448-.067 3.5',
|
|
50
|
+
'66 3.566 0 01-.322-.198c-.195-.135-.363-.332-.612-.465v-.005h-.005c-.4-.246-.616-.51',
|
|
51
|
+
'2-.686-.71-.07-.268-.005-.47.193-.6.224-.135.38-.271.483-.336.104-.074.143-.102.176-',
|
|
52
|
+
'.131h.002v-.003c.169-.202.436-.47.839-.601.139-.036.294-.065.466-.065zm2.8 2.142c.35',
|
|
53
|
+
'8 1.417 1.196 3.475 1.735 4.473.286.534.855 1.659 1.102 3.024.156-.005.33.018.513.06',
|
|
54
|
+
'4.646-1.671-.546-3.467-1.089-3.966-.22-.2-.232-.335-.123-.335.59.534 1.365 1.572 1.6',
|
|
55
|
+
'46 2.757.13.535.16 1.104.021 1.67.067.028.135.06.205.067 1.032.534 1.413.938 1.23 1.',
|
|
56
|
+
'537v-.043c-.06-.003-.12 0-.18 0h-.016c.151-.467-.182-.825-1.065-1.224-.915-.4-1.646-',
|
|
57
|
+
'.336-1.77.465-.008.043-.013.066-.018.135-.068.023-.139.053-.209.064-.43.268-.662.669',
|
|
58
|
+
'-.793 1.187-.13.533-.17 1.156-.205 1.869v.003c-.02.334-.17.838-.319 1.35-1.5 1.072-3',
|
|
59
|
+
'.58 1.538-5.348.334a2.645 2.645 0 00-.402-.533 1.45 1.45 0 00-.275-.333c.182 0 .338-',
|
|
60
|
+
'.03.465-.067a.615.615 0 00.314-.334c.108-.267 0-.697-.345-1.163-.345-.467-.931-.995-',
|
|
61
|
+
'1.788-1.521-.63-.4-.986-.87-1.15-1.396-.165-.534-.143-1.085-.015-1.645.245-1.07.873-',
|
|
62
|
+
'2.11 1.274-2.763.107-.065.037.135-.408.974-.396.751-1.14 2.497-.122 3.854a8.123 8.12',
|
|
63
|
+
'3 0 01.647-2.876c.564-1.278 1.743-3.504 1.836-5.268.048.036.217.135.289.202.218.133.',
|
|
64
|
+
'38.333.59.465.21.201.477.335.876.335.039.003.075.006.11.006.412 0 .73-.134.997-.268.',
|
|
65
|
+
'29-.134.52-.334.74-.4h.005c.467-.135.835-.402 1.044-.7zm2.185 8.958c.037.6.343 1.245',
|
|
66
|
+
'.882 1.377.588.134 1.434-.333 1.791-.765l.211-.01c.315-.007.577.01.847.268l.003.003c',
|
|
67
|
+
'.208.199.305.53.391.876.085.4.154.78.409 1.066.486.527.645.906.636 1.14l.003-.007v.0',
|
|
68
|
+
'18l-.003-.012c-.015.262-.185.396-.498.595-.63.401-1.746.712-2.457 1.57-.618.737-1.37',
|
|
69
|
+
' 1.14-2.036 1.191-.664.053-1.237-.2-1.574-.898l-.005-.003c-.21-.4-.12-1.025.056-1.69',
|
|
70
|
+
'.176-.668.428-1.344.463-1.897.037-.714.076-1.335.195-1.814.12-.465.308-.797.641-.984',
|
|
71
|
+
'l.045-.022zm-10.814.049h.01c.053 0 .105.005.157.014.376.055.706.333 1.023.752l.91 1.',
|
|
72
|
+
'664.003.003c.243.533.754 1.064 1.189 1.637.434.598.77 1.131.729 1.57v.006c-.057.744-',
|
|
73
|
+
'.48 1.148-1.125 1.294-.645.135-1.52.002-2.395-.464-.968-.536-2.118-.469-2.857-.602-.',
|
|
74
|
+
'369-.066-.61-.2-.723-.4-.11-.2-.113-.602.123-1.23v-.004l.002-.003c.117-.334.03-.752-',
|
|
75
|
+
'.027-1.118-.055-.401-.083-.71.043-.94.16-.334.396-.4.69-.533.294-.135.64-.202.915-.4',
|
|
76
|
+
'7h.002v-.002c.256-.268.445-.601.668-.838.19-.201.38-.336.663-.336zm7.159-9.074c-.435',
|
|
77
|
+
'.201-.945.535-1.488.535-.542 0-.97-.267-1.28-.466-.154-.134-.28-.268-.373-.335-.164-',
|
|
78
|
+
'.134-.144-.333-.074-.333.109.016.129.134.199.2.096.066.215.2.36.333.292.2.68.467 1.1',
|
|
79
|
+
'67.467.485 0 1.053-.267 1.398-.466.195-.135.445-.334.648-.467.156-.136.149-.267.279-',
|
|
80
|
+
'.267.128.016.034.134-.147.332a8.097 8.097 0 01-.69.468zm-1.082-1.583V5.64c-.006-.02.',
|
|
81
|
+
'013-.042.029-.05.074-.043.18-.027.26.004.063 0 .16.067.15.135-.006.049-.085.066-.135',
|
|
82
|
+
'.066-.055 0-.092-.043-.141-.068-.052-.018-.146-.008-.163-.065zm-.551 0c-.02.058-.113',
|
|
83
|
+
'.049-.166.066-.047.025-.086.068-.14.068-.05 0-.13-.02-.136-.068-.01-.066.088-.133.15',
|
|
84
|
+
'-.133.08-.031.184-.047.259-.005.019.009.036.03.03.05v.02h.003z',
|
|
85
|
+
].join('')
|
|
86
|
+
|
|
87
|
+
export const wandPath = [
|
|
88
|
+
'M15.1986 9.94447C14.7649 9.5337 14.4859 8.98613 14.4085 8.39384L14.0056 5.31138L11.2',
|
|
89
|
+
'75 6.79724C10.7503 7.08274 10.1433 7.17888 9.55608 7.06948L6.49998 6.50015L7.06931 9',
|
|
90
|
+
'.55625C7.17871 10.1435 7.08257 10.7505 6.79707 11.2751L5.31121 14.0057L8.39367 14.40',
|
|
91
|
+
'86C8.98596 14.4861 9.53353 14.7651 9.94431 15.1987L12.0821 17.4557L13.4178 14.6486C1',
|
|
92
|
+
'3.6745 14.1092 14.109 13.6747 14.6484 13.418L17.4555 12.0823L15.1986 9.94447ZM15.223',
|
|
93
|
+
'8 15.5079L13.0111 20.1581C12.8687 20.4573 12.5107 20.5844 12.2115 20.442C12.1448 20.',
|
|
94
|
+
'4103 12.0845 20.3665 12.0337 20.3129L8.49229 16.5741C8.39749 16.474 8.27113 16.4096 ',
|
|
95
|
+
'8.13445 16.3918L3.02816 15.7243C2.69958 15.6814 2.46804 15.3802 2.51099 15.0516C2.52',
|
|
96
|
+
'056 14.9784 2.54359 14.9075 2.5789 14.8426L5.04031 10.3192C5.1062 10.1981 5.12839 10',
|
|
97
|
+
'.058 5.10314 9.92253L4.16 4.85991C4.09931 4.53414 4.3142 4.22086 4.63997 4.16017C4.7',
|
|
98
|
+
'126 4.14664 4.78711 4.14664 4.85974 4.16017L9.92237 5.10331C10.0579 5.12855 10.198 5',
|
|
99
|
+
'.10637 10.319 5.04048L14.8424 2.57907C15.1335 2.42068 15.4979 2.52825 15.6562 2.8193',
|
|
100
|
+
'1C15.6916 2.88421 15.7146 2.95507 15.7241 3.02833L16.3916 8.13462C16.4095 8.2713 16.',
|
|
101
|
+
'4739 8.39766 16.5739 8.49245L20.3127 12.0338C20.5533 12.2617 20.5636 12.6415 20.3357',
|
|
102
|
+
' 12.8821C20.2849 12.9357 20.2246 12.9795 20.1579 13.0112L15.5078 15.224C15.3833 15.2',
|
|
103
|
+
'832 15.283 15.3835 15.2238 15.5079ZM16.0206 17.435L17.4348 16.0208L21.6775 20.2634L2',
|
|
104
|
+
'0.2633 21.6776L16.0206 17.435Z',
|
|
105
|
+
].join('')
|
package/src/chat/index.ts
CHANGED
|
@@ -1,2 +1,26 @@
|
|
|
1
|
-
// ChatPanel (docs/16 §4.2); it renders ToolCallItem, ApprovalCard and SkillPicker itself.
|
|
2
1
|
export { default as ChatPanel } from './ChatPanel.svelte'
|
|
2
|
+
export { default as ChatSurface } from './ChatSurface.svelte'
|
|
3
|
+
export { default as ChatComposer } from './ChatComposer.svelte'
|
|
4
|
+
export { default as ChatMessage } from './ChatMessage.svelte'
|
|
5
|
+
export { default as ReasoningControl } from './ReasoningControl.svelte'
|
|
6
|
+
export { default as UsageControl } from './UsageControl.svelte'
|
|
7
|
+
export { default as ModelEffortPicker } from './ModelEffortPicker.svelte'
|
|
8
|
+
export { default as ContextUsageStatus } from './ContextUsageStatus.svelte'
|
|
9
|
+
export { default as ReasoningSlider } from './ReasoningSlider.svelte'
|
|
10
|
+
export {
|
|
11
|
+
groupModels,
|
|
12
|
+
modelKey,
|
|
13
|
+
nextValue,
|
|
14
|
+
parseQuery,
|
|
15
|
+
pickerModels,
|
|
16
|
+
type PickerModel,
|
|
17
|
+
type PickerValue,
|
|
18
|
+
} from './model-effort'
|
|
19
|
+
export {
|
|
20
|
+
compact,
|
|
21
|
+
detectPlatform,
|
|
22
|
+
pressure,
|
|
23
|
+
spending,
|
|
24
|
+
type Platform,
|
|
25
|
+
type SpendingTurn,
|
|
26
|
+
} from './context-status'
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Pure rules for the composer model/effort picker (docs/31 §3–§4.2): search, ranking,
|
|
2
|
+
// carry-over of an explicit effort and the secret-free row projection. No I/O.
|
|
3
|
+
import type { KitI18n } from '../i18n'
|
|
4
|
+
import type { ModelOption, Provider, ReasoningEffort } from '../ports'
|
|
5
|
+
import { REASONING_EFFORTS } from '../providers/reasoning'
|
|
6
|
+
|
|
7
|
+
/** One selectable model as the picker shows it; `key` stays an opaque host identity. */
|
|
8
|
+
export interface PickerModel {
|
|
9
|
+
key: string
|
|
10
|
+
providerId: string
|
|
11
|
+
provider: string
|
|
12
|
+
name: string
|
|
13
|
+
/** Route, price and context facts; empty parts are omitted, never guessed. */
|
|
14
|
+
detail: string
|
|
15
|
+
/** The route has an enabled, readable key. Keyless models stay selectable. */
|
|
16
|
+
hasKey: boolean
|
|
17
|
+
/** Declared levels in display order, without `default`. */
|
|
18
|
+
efforts: ReasoningEffort[]
|
|
19
|
+
contextWindow: number | null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** `effort: 'default'` sends no reasoning control; it is not `none`. */
|
|
23
|
+
export interface PickerValue {
|
|
24
|
+
key: string
|
|
25
|
+
effort: ReasoningEffort
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const modelKey = (endpointId: string, modelId: string) => `${endpointId}\n${modelId}`
|
|
29
|
+
|
|
30
|
+
/** Splits a trailing level (`opus high`); `off` is accepted for `none`. */
|
|
31
|
+
export function parseQuery(query: string): { text: string; effort: ReasoningEffort | null } {
|
|
32
|
+
const trimmed = query.trim()
|
|
33
|
+
const match = /^(.*\S)\s+(\S+)$/.exec(trimmed)
|
|
34
|
+
if (match?.[1] && match[2]) {
|
|
35
|
+
const word = match[2].toLowerCase()
|
|
36
|
+
const effort = word === 'off' ? 'none' : REASONING_EFFORTS.find((item) => item === word)
|
|
37
|
+
if (effort) return { text: match[1].trim(), effort }
|
|
38
|
+
}
|
|
39
|
+
return { text: trimmed, effort: null }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Lower is better; `null` hides the model (no match, or the typed level is not declared). */
|
|
43
|
+
export function score(model: PickerModel, query: string): number | null {
|
|
44
|
+
const { text, effort } = parseQuery(query)
|
|
45
|
+
if (effort && !model.efforts.includes(effort)) return null
|
|
46
|
+
const haystack = `${model.name} ${model.provider} ${model.detail}`.toLowerCase()
|
|
47
|
+
const needle = text.toLowerCase()
|
|
48
|
+
const index = haystack.indexOf(needle)
|
|
49
|
+
if (index >= 0) return index
|
|
50
|
+
const parts = needle.split(/\s+/).filter(Boolean)
|
|
51
|
+
if (parts.every((part) => haystack.includes(part))) return 500
|
|
52
|
+
let rank = 1000
|
|
53
|
+
let from = 0
|
|
54
|
+
for (const char of needle.replace(/\s+/g, '')) {
|
|
55
|
+
const found = haystack.indexOf(char, from)
|
|
56
|
+
if (found < 0) return null
|
|
57
|
+
rank += found
|
|
58
|
+
from = found + 1
|
|
59
|
+
}
|
|
60
|
+
return rank
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Typed level first, then the current explicit level if declared, else `default`. */
|
|
64
|
+
export function nextValue(
|
|
65
|
+
model: PickerModel,
|
|
66
|
+
current: PickerValue | null,
|
|
67
|
+
query: string,
|
|
68
|
+
): PickerValue {
|
|
69
|
+
const typed = parseQuery(query).effort
|
|
70
|
+
if (typed && model.efforts.includes(typed)) return { key: model.key, effort: typed }
|
|
71
|
+
const kept = current?.effort
|
|
72
|
+
const effort = kept && kept !== 'default' && model.efforts.includes(kept) ? kept : 'default'
|
|
73
|
+
return { key: model.key, effort }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface PickerGroup {
|
|
77
|
+
id: string
|
|
78
|
+
provider: string
|
|
79
|
+
models: PickerModel[]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Groups in first-seen order; models inside a group are ranked when searching. */
|
|
83
|
+
export function groupModels(models: readonly PickerModel[], query: string): PickerGroup[] {
|
|
84
|
+
const searching = query.trim() !== ''
|
|
85
|
+
const groups: PickerGroup[] = []
|
|
86
|
+
for (const model of models) {
|
|
87
|
+
if (searching && score(model, query) === null) continue
|
|
88
|
+
const group = groups.find((item) => item.id === model.providerId)
|
|
89
|
+
if (group) group.models.push(model)
|
|
90
|
+
else groups.push({ id: model.providerId, provider: model.provider, models: [model] })
|
|
91
|
+
}
|
|
92
|
+
if (searching)
|
|
93
|
+
for (const group of groups)
|
|
94
|
+
group.models.sort((a, b) => (score(a, query) ?? Infinity) - (score(b, query) ?? Infinity))
|
|
95
|
+
return groups
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Header facts: key presence, the first model's route and the model count. */
|
|
99
|
+
export function groupSummary(group: PickerGroup, i18n: KitI18n): string {
|
|
100
|
+
const route = group.models[0]?.detail.split(' · ')[0]?.trim() ?? ''
|
|
101
|
+
const keyed = group.models.some((model) => model.hasKey)
|
|
102
|
+
const head = [keyed ? i18n.t('chat.picker.keyed') : '', route].filter(Boolean).join(' · ')
|
|
103
|
+
return `${head} ${group.models.length}`.trim()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Filled bars (0–4) of the trigger; `none` is not a reasoning rank. */
|
|
107
|
+
export function effortBars(model: PickerModel, effort: ReasoningEffort): number {
|
|
108
|
+
if (effort === 'default' || effort === 'none') return 0
|
|
109
|
+
const ranked = model.efforts.filter((item) => item !== 'none')
|
|
110
|
+
const rank = ranked.indexOf(effort)
|
|
111
|
+
if (rank < 0) return 0
|
|
112
|
+
return Math.round(((rank + 1) / ranked.length) * 4)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Color family of a level: fast (info), deep (warning) or regular (primary). */
|
|
116
|
+
export function effortTone(effort: ReasoningEffort): 'fast' | 'deep' | 'regular' {
|
|
117
|
+
if (effort === 'none' || effort === 'minimal' || effort === 'low') return 'fast'
|
|
118
|
+
if (effort === 'xhigh' || effort === 'max') return 'deep'
|
|
119
|
+
return 'regular'
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function contextSize(tokens: number): string {
|
|
123
|
+
if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(1).replace(/\.0$/, '')}M`
|
|
124
|
+
if (tokens >= 1000) return `${Math.floor(tokens / 1000)}K`
|
|
125
|
+
return String(tokens)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Joins options with provider metadata for the second row. Without `providers` the picker still
|
|
130
|
+
* works; route names fall back to the option label, prices and windows are left out.
|
|
131
|
+
*/
|
|
132
|
+
export function pickerModels(
|
|
133
|
+
options: readonly ModelOption[],
|
|
134
|
+
providers: readonly Provider[] | null,
|
|
135
|
+
i18n: KitI18n,
|
|
136
|
+
): PickerModel[] {
|
|
137
|
+
return options.map((option) => {
|
|
138
|
+
const segments = option.label.split(' · ')
|
|
139
|
+
let provider: Provider | undefined
|
|
140
|
+
let endpoint: Provider['endpoints'][number] | undefined
|
|
141
|
+
for (const item of providers ?? []) {
|
|
142
|
+
endpoint = item.endpoints.find((candidate) => candidate.id === option.endpointId)
|
|
143
|
+
if (endpoint) {
|
|
144
|
+
provider = item
|
|
145
|
+
break
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const model = endpoint?.models.find((candidate) => candidate.id === option.modelId)
|
|
149
|
+
const name = option.name?.trim() || option.modelId
|
|
150
|
+
const route = endpoint?.slug ?? segments[1] ?? ''
|
|
151
|
+
const parts = [route]
|
|
152
|
+
if (name !== option.modelId) parts.push(option.modelId)
|
|
153
|
+
const tier = model?.pricing?.tiers[0]
|
|
154
|
+
if (model?.pricing && tier)
|
|
155
|
+
parts.push(
|
|
156
|
+
i18n.t('chat.picker.price', {
|
|
157
|
+
currency: model.pricing.currency,
|
|
158
|
+
input: tier.input,
|
|
159
|
+
output: tier.output,
|
|
160
|
+
}),
|
|
161
|
+
)
|
|
162
|
+
const contextWindow = model?.contextWindow ?? null
|
|
163
|
+
if (contextWindow)
|
|
164
|
+
parts.push(i18n.t('chat.picker.context', { size: contextSize(contextWindow) }))
|
|
165
|
+
const declared = option.reasoning?.efforts ?? []
|
|
166
|
+
return {
|
|
167
|
+
key: modelKey(option.endpointId, option.modelId),
|
|
168
|
+
providerId: provider?.id ?? segments[0] ?? option.endpointId,
|
|
169
|
+
provider: provider?.name ?? segments[0] ?? option.endpointId,
|
|
170
|
+
name,
|
|
171
|
+
detail: parts.filter(Boolean).join(' · '),
|
|
172
|
+
hasKey: endpoint?.keys.some((key) => key.enabled && key.readable) ?? false,
|
|
173
|
+
efforts: REASONING_EFFORTS.filter((effort) => declared.includes(effort)),
|
|
174
|
+
contextWindow,
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Cost, TokenUsage, Turn } from '../ports'
|
|
2
|
+
import type { ChatTurn } from './run-session-types'
|
|
3
|
+
|
|
4
|
+
export function fromHistory(turn: Turn): ChatTurn {
|
|
5
|
+
return {
|
|
6
|
+
id: turn.id,
|
|
7
|
+
persisted: true,
|
|
8
|
+
parentId: turn.parentId,
|
|
9
|
+
questionId: turn.questionId,
|
|
10
|
+
supersedesId: turn.supersedesId,
|
|
11
|
+
kind: turn.kind,
|
|
12
|
+
endpointId: turn.endpointId,
|
|
13
|
+
modelId: turn.modelId,
|
|
14
|
+
input: turn.input,
|
|
15
|
+
images: turn.images,
|
|
16
|
+
generated: turn.generated,
|
|
17
|
+
text: turn.text,
|
|
18
|
+
reasoningText: turn.reasoningText ?? '',
|
|
19
|
+
reasoningEffort: turn.reasoningEffort ?? 'default',
|
|
20
|
+
usage: turn.usage ?? null,
|
|
21
|
+
cost: turn.cost ?? null,
|
|
22
|
+
contextUsage: turn.contextUsage ?? null,
|
|
23
|
+
status: turn.status,
|
|
24
|
+
error: turn.errorCode,
|
|
25
|
+
tools: [],
|
|
26
|
+
exhausted: false,
|
|
27
|
+
search: { status: 'idle', query: null, provider: null, hits: [], error: null },
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function cumulativeUsage(turns: readonly ChatTurn[]): TokenUsage {
|
|
32
|
+
const total: TokenUsage = {
|
|
33
|
+
inputTokens: null,
|
|
34
|
+
outputTokens: null,
|
|
35
|
+
cacheReadTokens: null,
|
|
36
|
+
cacheWriteTokens: null,
|
|
37
|
+
reasoningTokens: null,
|
|
38
|
+
}
|
|
39
|
+
for (const turn of turns)
|
|
40
|
+
for (const key of Object.keys(total) as (keyof TokenUsage)[]) {
|
|
41
|
+
const value = turn.usage?.[key]
|
|
42
|
+
if (value != null) total[key] = (total[key] ?? 0) + value
|
|
43
|
+
}
|
|
44
|
+
return total
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function cumulativeCost(turns: readonly ChatTurn[]): Cost[] {
|
|
48
|
+
const totals: Cost[] = []
|
|
49
|
+
for (const { cost } of turns) {
|
|
50
|
+
if (!cost) continue
|
|
51
|
+
const found = totals.find((item) => item.currency === cost.currency)
|
|
52
|
+
if (found) found.amount += cost.amount
|
|
53
|
+
else totals.push({ ...cost })
|
|
54
|
+
}
|
|
55
|
+
return totals
|
|
56
|
+
}
|