mirror of
https://github.com/abue-ammar/tinycast.git
synced 2026-10-02 08:14:38 +08:00
* Show AI providers as a list beside the selected provider's detail The Providers panel was one long form: every installed tool and API connection stacked as rows, with status, quota and actions squeezed into each. It now follows Mail's Accounts: the on-device model, the installed commands and the API connections sit in a list, each with a one-line state and, for installed tools, its switch. The selected provider shows its status, account, usage, command path and models beside it, with a filter once a list runs long. API connections are added from the list's plus menu, removed with its minus, and still edited in the existing connection editor. * Choose which of a provider's models the model picker lists A route like OpenCode offers hundreds of models, and every one of them landed in the chat's model picker. Each provider's model list is now a set of checkboxes, with Show All and Hide All above it. A provider nobody has trimmed keeps listing everything, including models it adds later; once trimmed, it lists only what was ticked. The default model always stays listed, since the picker has to show what it holds, and a removed connection forgets its choices. The choice is per Mac and stays out of settings backups. The provider switch moves from the list into the selected provider's header, so it no longer sits on the list's blue selection, and model rows drop their effort levels, which the chat's own picker already shows. * Give Grok, Cursor and OpenCode their own marks Grok borrowed X's mark, Cursor a pointer symbol and OpenCode a terminal symbol. Each now has its own, in the Providers list and in the chat's model picker. OpenCode's inner block is drawn with `opacity`: the asset compiler drops `fill-opacity` without a warning, which filled the block solid and turned the mark into a window with a title bar. * Name ChatGPT's newer plans Pro now comes in two sizes, which the account reports as "pro" and "prolite" and ChatGPT sells as Pro 20x and Pro 5x. Business, Enterprise and Edu each gained plan types that read as "Account". * List a provider's models in a table A Form realizes every row it holds, and OpenCode offers over four hundred models, so its list stuttered while scrolling. The checkboxes now sit in an NSTableView inside one Form row, which builds only the rows on screen. * Switch the on-device model or an API connection off in place Only an installed tool could be switched off; an API connection had to be removed, key and all, to leave the model picker. The on-device model and every API connection now have the same switch. A route that is off stays configured but leaves every picker, the default model moves to a route still on, and asking for it anyway is refused with a message. Removing a connection forgets that it was off, and the choice stays out of backups. * Launch an installed tool from a set path, with set variables Tinycast finds each installed tool by asking a login shell, which is right until a Mac has two copies or a shell it cannot read. A tool can now carry a command path that replaces the lookup, and variables set for that tool each time it starts. Nothing sets them yet; the next commit adds the fields. A path with nothing to run fails the check and the turn, never falling back to the lookup, since that would hide the mistake the path was set to fix. The reader's variables lie over the app's own, but never over one Tinycast sets to keep a tool inside the chat. Names are stored in settings and values in the login Keychain; neither travels in a backup, which must not decide what this Mac launches. Only the tool that was edited is checked again. * Add an Advanced section to an installed tool's detail The command path has a field and a Choose… button whose open panel shows hidden folders, since `~/.local/bin` is one. Variables are rows of a name and a hidden value, added and removed one at a time. A name Tinycast sets itself says its value is not used, and one that is not a variable name says why. Each field saves as it is left. * Name a Claude model by its display name when the description has no version Claude Code used to lead a model's description with its version, "Opus 5.5 · Best for everyday…", and Tinycast named the model from that. A newer CLI puts the version in the display name and leaves only the blurb in the description, so every model in the picker read "Claude Best for everyday, complex tasks". The version is now taken from the description only when it is there, and from the display name otherwise. * End an edit in the Providers panel with a click outside the field A field holds the keyboard until something else takes it, and blank form space takes nothing. The panel now releases it the way every other pane does, which is also what saves a field that is saved as it is left. * Show Claude's account and Codex's command, and keep Advanced while a tool is off Claude's detail now names the account it is signed in with and its plan, the address hidden until clicked as Codex's is. Both come from the answer that already lists Claude's models, so nothing more is asked of the CLI. Codex's detail names the command it ran, which the other tools already did, and the empty command path field shows it too. A tool that is switched off keeps its Advanced section, so a wrong path can be fixed before it is switched on. * Give a provider's detail its own pages A provider's status, its models and its advanced settings shared one scrolling form, so OpenCode's four hundred models buried everything under them. The detail now has Overview, Models and Advanced pages behind a segmented control, as Mail's Accounts has. An API connection has the first two, and the on-device model, with one page, shows no control. The chosen page is kept from one provider to the next. * Keep the accent colour on the providers panel's page control An editor panel wraps its content in Liquid Glass, and a segmented control drawn on glass goes grey and bordered. The divider beside the selected page is always hidden, so in grey the control read as missing one. The providers panel now draws its glass behind the content, which leaves the control its accent colour; every other editor panel is unchanged. * Keep the MCP editor's connection control from resizing The system segmented control opens tight around its labels and widens the first time its selection changes, so the HTTP and Command control grew under the pointer and stayed that wide. The editor now uses the AppKit control with each segment pinned to its label plus the inset the control settles at, and reports its own size from those widths, so it opens at the size it would have ended up. * Say Automatic in an empty command path field The empty field showed the path the lookup had found, which read as a path already set, and repeated the Command row on Overview. It now says Automatic; the found path is on Overview alone. * Credit the three new marks, and take two from the sources already credited Cursor's mark now comes from Simple Icons and Grok's from Lobe Icons, the two sources NOTICE already names; both look as they did. OpenCode's is drawn after the one in its own repository, since the libraries carry the frame without the block inside it, and its licence travels with it. * Document the Providers panel, its pages and a tool's overrides The feature doc gains the rules that are now invariants: a route that is off is off everywhere, a picker lists what was ticked, a set command path wins or fails, and a reader's variable never replaces one Tinycast sets. It describes the panel's list and pages, and says why three more AI keys stay out of backups and settings.json. The UI doc records the panel's sizes and the two things learned about controls in an editor panel. The website explains the pages, the switch, and the Advanced page. * Keep a tool's variables when the Keychain cannot be read A failed read loaded the variables' names with blank values, and leaving any field in Advanced then saved the blanks over the stored values. The read now throws, the page says it could not read them and saves only the command path, and a save whose check read fails is refused.
84 lines
2.4 KiB
Swift
84 lines
2.4 KiB
Swift
import Foundation
|
|
|
|
enum ChatGPTSubscription {
|
|
enum Phase: Equatable, Sendable {
|
|
case idle
|
|
case starting
|
|
case signedOut
|
|
case connected
|
|
case unavailable(String)
|
|
case failed(String)
|
|
|
|
var isUnavailable: Bool {
|
|
if case .unavailable = self { return true }
|
|
return false
|
|
}
|
|
}
|
|
|
|
struct Account: Equatable, Sendable {
|
|
let email: String?
|
|
let plan: String
|
|
|
|
var planTitle: String {
|
|
switch plan {
|
|
case "free": return "Free"
|
|
case "go": return "Go"
|
|
case "plus": return "Plus"
|
|
case "pro": return "Pro 20x"
|
|
case "prolite": return "Pro 5x"
|
|
case "team": return "Team"
|
|
case "self_serve_business_prolite", "self_serve_business_usage_based", "business":
|
|
return "Business"
|
|
case "ent26", "enterprise_cbp_automation", "enterprise_cbp_usage_based", "enterprise":
|
|
return "Enterprise"
|
|
case "edu", "edu_plus", "edu_pro": return "Edu"
|
|
case "apiKey", "api_key": return "API key"
|
|
default: return "Account"
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Effort: Equatable, Identifiable, Sendable {
|
|
let id: String
|
|
let detail: String?
|
|
|
|
var title: String {
|
|
switch id {
|
|
case "xhigh": return "Extra high"
|
|
case "minimal": return "Minimal"
|
|
default: return id.prefix(1).uppercased() + id.dropFirst()
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Model: Equatable, Identifiable, Sendable {
|
|
let id: String
|
|
let name: String
|
|
let efforts: [Effort]
|
|
let defaultEffort: String?
|
|
let isDefault: Bool
|
|
|
|
func resolvedEffort(_ preferred: String?) -> String? {
|
|
guard !efforts.isEmpty else { return nil }
|
|
if let preferred, efforts.contains(where: { $0.id == preferred }) { return preferred }
|
|
if let defaultEffort, efforts.contains(where: { $0.id == defaultEffort }) {
|
|
return defaultEffort
|
|
}
|
|
return efforts.first?.id
|
|
}
|
|
}
|
|
|
|
struct UsageWindow: Equatable, Sendable {
|
|
let usedPercent: Int
|
|
let durationMinutes: Int?
|
|
let resetsAt: Date?
|
|
|
|
var remainingPercent: Int { max(0, min(100, 100 - usedPercent)) }
|
|
}
|
|
|
|
struct RateLimits: Equatable, Sendable {
|
|
let primary: UsageWindow?
|
|
let secondary: UsageWindow?
|
|
}
|
|
}
|