b2970e4c-45a2-423e-b84d-257.../Sources/FundPlugin.swift
徐翔宇 63c7c0bc60 feat(ui): toast + confirm dialog + list animations
Add operation feedback across the three interactive paths in 看盘侠:
- Toast: ObservableObject + view modifier, lime/red/yellow palette
  matching FundTheme. Auto-dismiss after 2.6s, click to dismiss.
- ConfirmDialog: programmatic ask() returning async Bool via a
  captured CheckedContinuation. Danger flag flips the confirm button
  to a red palette.
- HoldingsView: row remove now asks for confirmation (the trash UI
  is a hover-only ✕ — easy to misclick); on confirm, row slides out
  to the leading edge with a spring + a success toast lands.
- HoldingsView: position edit Save / Clear surface the right
  semantic toast (success for save, info for clear).
- AddView: adding a fund toasts the name; tapping an already-added
  row toasts an info reminder rather than silently no-op'ing.

Bump version 0.2.0 → 0.3.0 (Info.plist + FundPlugin.version).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 20:29:00 +08:00

53 lines
1.4 KiB
Swift

//
// FundPlugin.swift
// Mio Island plugin:
//
// Principal class. Module = FundPlugin, Class = FundPlugin
// NSPrincipalClass = "FundPlugin.FundPlugin".
//
import AppKit
import SwiftUI
final class FundPlugin: NSObject, MioPlugin {
var id: String { "fund" }
var name: String { "看盘侠" }
var icon: String { "chart.line.uptrend.xyaxis" }
var version: String { "0.3.0" }
func activate() {
FundDebugLog.write("plugin activate")
// Kick off the refresh loops as soon as the plugin enables.
// The store is a singleton so the loops survive panel show/hide.
Task { @MainActor in
FundStore.shared.start()
await FundStore.shared.refreshNow()
}
}
func deactivate() {
FundDebugLog.write("plugin deactivate")
Task { @MainActor in
FundStore.shared.stop()
}
}
func makeView() -> NSView {
let view = NSHostingView(rootView: ExpandedView())
view.autoresizingMask = [.width, .height]
return view
}
@objc func viewForSlot(_ slot: String, context: [String: Any]) -> NSView? {
switch slot {
case "header":
let v = NSHostingView(rootView: HeaderSlotView())
v.frame = NSRect(x: 0, y: 0, width: 20, height: 20)
v.setFrameSize(NSSize(width: 20, height: 20))
return v
default:
return nil
}
}
}