entry.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. // @refresh reload
  2. import { render } from "solid-js/web"
  3. import { AppBaseProviders, AppInterface } from "@/app"
  4. import { Platform, PlatformProvider } from "@/context/platform"
  5. import { dict as en } from "@/i18n/en"
  6. import { dict as zh } from "@/i18n/zh"
  7. import pkg from "../package.json"
  8. const DEFAULT_SERVER_URL_KEY = "opencode.settings.dat:defaultServerUrl"
  9. const getLocale = () => {
  10. if (typeof navigator !== "object") return "en" as const
  11. const languages = navigator.languages?.length ? navigator.languages : [navigator.language]
  12. for (const language of languages) {
  13. if (!language) continue
  14. if (language.toLowerCase().startsWith("zh")) return "zh" as const
  15. }
  16. return "en" as const
  17. }
  18. const getRootNotFoundError = () => {
  19. const key = "error.dev.rootNotFound" as const
  20. const locale = getLocale()
  21. return locale === "zh" ? (zh[key] ?? en[key]) : en[key]
  22. }
  23. const getStorage = (key: string) => {
  24. if (typeof localStorage === "undefined") return null
  25. try {
  26. return localStorage.getItem(key)
  27. } catch {
  28. return null
  29. }
  30. }
  31. const setStorage = (key: string, value: string | null) => {
  32. if (typeof localStorage === "undefined") return
  33. try {
  34. if (value !== null) {
  35. localStorage.setItem(key, value)
  36. return
  37. }
  38. localStorage.removeItem(key)
  39. } catch {
  40. return
  41. }
  42. }
  43. const readDefaultServerUrl = () => getStorage(DEFAULT_SERVER_URL_KEY)
  44. const writeDefaultServerUrl = (url: string | null) => setStorage(DEFAULT_SERVER_URL_KEY, url)
  45. const notify: Platform["notify"] = async (title, description, href) => {
  46. if (!("Notification" in window)) return
  47. const permission =
  48. Notification.permission === "default"
  49. ? await Notification.requestPermission().catch(() => "denied")
  50. : Notification.permission
  51. if (permission !== "granted") return
  52. const inView = document.visibilityState === "visible" && document.hasFocus()
  53. if (inView) return
  54. const notification = new Notification(title, {
  55. body: description ?? "",
  56. icon: "https://opencode.ai/favicon-96x96-v3.png",
  57. })
  58. notification.onclick = () => {
  59. window.focus()
  60. if (href) {
  61. window.history.pushState(null, "", href)
  62. window.dispatchEvent(new PopStateEvent("popstate"))
  63. }
  64. notification.close()
  65. }
  66. }
  67. const openLink: Platform["openLink"] = (url) => {
  68. window.open(url, "_blank")
  69. }
  70. const back: Platform["back"] = () => {
  71. window.history.back()
  72. }
  73. const forward: Platform["forward"] = () => {
  74. window.history.forward()
  75. }
  76. const restart: Platform["restart"] = async () => {
  77. window.location.reload()
  78. }
  79. const root = document.getElementById("root")
  80. if (!(root instanceof HTMLElement) && import.meta.env.DEV) {
  81. throw new Error(getRootNotFoundError())
  82. }
  83. const platform: Platform = {
  84. platform: "web",
  85. version: pkg.version,
  86. openLink,
  87. back,
  88. forward,
  89. restart,
  90. notify,
  91. getDefaultServerUrl: readDefaultServerUrl,
  92. setDefaultServerUrl: writeDefaultServerUrl,
  93. }
  94. if (root instanceof HTMLElement) {
  95. render(
  96. () => (
  97. <PlatformProvider value={platform}>
  98. <AppBaseProviders>
  99. <AppInterface />
  100. </AppBaseProviders>
  101. </PlatformProvider>
  102. ),
  103. root,
  104. )
  105. }