loading.tsx 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import { MetaProvider } from "@solidjs/meta"
  2. import { render } from "solid-js/web"
  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 type { InitStep, SqliteMigrationProgress } from "../preload/types"
  10. const root = document.getElementById("root")!
  11. const lines = ["Just a moment...", "Migrating your database", "This may take a couple of minutes"]
  12. const delays = [3000, 9000]
  13. render(() => {
  14. const [step, setStep] = createSignal<InitStep | null>(null)
  15. const [line, setLine] = createSignal(0)
  16. const [percent, setPercent] = createSignal(0)
  17. const phase = createMemo(() => step()?.phase)
  18. const value = createMemo(() => {
  19. if (phase() === "done") return 100
  20. return Math.max(25, Math.min(100, percent()))
  21. })
  22. window.api.awaitInitialization((next) => setStep(next as InitStep)).catch(() => undefined)
  23. onMount(() => {
  24. setLine(0)
  25. setPercent(0)
  26. const timers = delays.map((ms, i) => setTimeout(() => setLine(i + 1), ms))
  27. const listener = window.api.onSqliteMigrationProgress((progress: SqliteMigrationProgress) => {
  28. if (progress.type === "InProgress") setPercent(Math.max(0, Math.min(100, progress.value)))
  29. if (progress.type === "Done") {
  30. setPercent(100)
  31. setStep({ phase: "done" })
  32. }
  33. })
  34. onCleanup(() => {
  35. listener()
  36. timers.forEach(clearTimeout)
  37. })
  38. })
  39. createEffect(() => {
  40. if (phase() !== "done") return
  41. const timer = setTimeout(() => window.api.loadingWindowComplete(), 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)