loading.tsx 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import { render } from "solid-js/web"
  2. import { MetaProvider } from "@solidjs/meta"
  3. import "@opencode-ai/app/index.css"
  4. import { Font } from "@opencode-ai/ui/font"
  5. import { Splash } from "@opencode-ai/ui/logo"
  6. import { Progress } from "@opencode-ai/ui/progress"
  7. import "./styles.css"
  8. import { createEffect, createMemo, createSignal, onCleanup, onMount } from "solid-js"
  9. import { commands, events, InitStep } from "./bindings"
  10. import { Channel } from "@tauri-apps/api/core"
  11. const root = document.getElementById("root")!
  12. const lines = ["Just a moment...", "Migrating your database", "This may take a couple of minutes"]
  13. const delays = [3000, 9000]
  14. render(() => {
  15. const [step, setStep] = createSignal<InitStep | null>(null)
  16. const [line, setLine] = createSignal(0)
  17. const [percent, setPercent] = createSignal(0)
  18. const phase = createMemo(() => step()?.phase)
  19. const value = createMemo(() => {
  20. if (phase() === "done") return 100
  21. return Math.max(25, Math.min(100, percent()))
  22. })
  23. const channel = new Channel<InitStep>()
  24. channel.onmessage = (next) => setStep(next)
  25. commands.awaitInitialization(channel as any).catch(() => undefined)
  26. onMount(() => {
  27. setLine(0)
  28. setPercent(0)
  29. const timers = delays.map((ms, i) => setTimeout(() => setLine(i + 1), ms))
  30. const listener = events.sqliteMigrationProgress.listen((e) => {
  31. if (e.payload.type === "InProgress") setPercent(Math.max(0, Math.min(100, e.payload.value)))
  32. if (e.payload.type === "Done") setPercent(100)
  33. })
  34. onCleanup(() => {
  35. listener.then((cb) => cb())
  36. timers.forEach(clearTimeout)
  37. })
  38. })
  39. createEffect(() => {
  40. if (phase() !== "done") return
  41. const timer = setTimeout(() => events.loadingWindowComplete.emit(null), 1000)
  42. onCleanup(() => clearTimeout(timer))
  43. })
  44. const status = createMemo(() => {
  45. if (phase() === "done") return "All done"
  46. if (phase() === "sqlite_waiting") return lines[line()]
  47. return "Just a moment..."
  48. })
  49. return (
  50. <MetaProvider>
  51. <div class="w-screen h-screen bg-background-base flex items-center justify-center">
  52. <Font />
  53. <div class="flex flex-col items-center gap-11">
  54. <Splash class="w-20 h-25 opacity-15" />
  55. <div class="w-60 flex flex-col items-center gap-4" aria-live="polite">
  56. <span class="w-full overflow-hidden text-center text-ellipsis whitespace-nowrap text-text-strong text-14-normal">
  57. {status()}
  58. </span>
  59. <Progress
  60. value={value()}
  61. class="w-20 [&_[data-slot='progress-track']]:h-1 [&_[data-slot='progress-track']]:border-0 [&_[data-slot='progress-track']]:rounded-none [&_[data-slot='progress-track']]:bg-surface-weak [&_[data-slot='progress-fill']]:rounded-none [&_[data-slot='progress-fill']]:bg-icon-warning-base"
  62. aria-label="Database migration progress"
  63. getValueLabel={({ value }) => `${Math.round(value)}%`}
  64. />
  65. </div>
  66. </div>
  67. </div>
  68. </MetaProvider>
  69. )
  70. }, root)