entry.tsx 3.0 KB

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