Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions nix/hashes.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-AD5kt1o035hdvYPyrvjiz2mcsXFJ7UJVEmbE9AJFDFs=",
"aarch64-linux": "sha256-f1gVXUpIRWC/I8dxIigdGGvPsju04MXVjZhtq5EYL64=",
"aarch64-darwin": "sha256-c+JRpjs2gsgQ2czyM6bTbYEdMbX/erOSQdX6TdrI59k=",
"x86_64-darwin": "sha256-iB7l4PtQQ9Mx/XAdtyayiLe6pZKhuXB9lpmrbTr+uWc="
"x86_64-linux": "sha256-SiJd6uXrL/MqqFGN/uUcHh0Wzdlafnpx++VZa5gUCoE=",
"aarch64-linux": "sha256-pKcT34NYIHVasraRTx0ASTzyuFuIzBTxXg6+KSKvTps=",
"aarch64-darwin": "sha256-hzrym0KpiyYAv80eT/DjcSIJWYBUq4QDgDf6Wtot7jU=",
"x86_64-darwin": "sha256-Or5dSTdajUwij0XpovXSXjJ0LmRUcpcW5TJ4q4B0k0A="
}
}
22 changes: 13 additions & 9 deletions packages/app/src/components/dialog-connect-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
} from "solid-js"
import { createStore, produce } from "solid-js/store"
import { useParams } from "@solidjs/router"
import { Link } from "@/components/link"
import { ExternalLink } from "@/components/external-link"
import { useServerSDK } from "@/context/server-sdk"
import { useServerSync } from "@/context/server-sync"
import { useLanguage } from "@/context/language"
Expand Down Expand Up @@ -836,12 +836,12 @@ function ProviderConnection(props: {
<div>{language.t("provider.connect.opencodeZen.line2")}</div>
<div>
{language.t("provider.connect.opencodeZen.visit.prefix")}
<Link
<ExternalLink
href="https://opencode.ai/zen"
class="text-v2-text-text-base focus-visible:rounded-xs focus-visible:outline-2 focus-visible:outline-v2-border-border-focus"
>
{language.t("provider.connect.opencodeZen.visit.link")}
</Link>
</ExternalLink>
{language.t("provider.connect.opencodeZen.visit.suffix")}
</div>
</div>
Expand Down Expand Up @@ -886,9 +886,9 @@ function ProviderConnection(props: {
<div class="text-14-regular text-text-base">{language.t("provider.connect.opencodeZen.line2")}</div>
<div class="text-14-regular text-text-base">
{language.t("provider.connect.opencodeZen.visit.prefix")}
<Link href="https://opencode.ai/zen" tabIndex={-1}>
<ExternalLink href="https://opencode.ai/zen" tabIndex={-1}>
{language.t("provider.connect.opencodeZen.visit.link")}
</Link>
</ExternalLink>
{language.t("provider.connect.opencodeZen.visit.suffix")}
</div>
</div>
Expand Down Expand Up @@ -967,9 +967,9 @@ function ProviderConnection(props: {
<div class="flex flex-col gap-5 px-3 text-[13px] font-[440] leading-5 tracking-[-0.04px] text-v2-text-text-muted">
<div>
{language.t("provider.connect.oauth.code.visit.prefix")}
<Link href={store.authorization!.url} class="text-v2-text-text-base">
<ExternalLink href={store.authorization!.url} class="text-v2-text-text-base">
{language.t("provider.connect.oauth.code.visit.link")}
</Link>
</ExternalLink>
{language.t("provider.connect.oauth.code.visit.suffix", { provider: provider().name })}
</div>
<form onSubmit={handleSubmit} class="flex flex-col items-start gap-5 self-stretch">
Expand Down Expand Up @@ -1006,7 +1006,9 @@ function ProviderConnection(props: {
<div class="flex flex-col gap-6">
<div class="text-14-regular text-text-base">
{language.t("provider.connect.oauth.code.visit.prefix")}
<Link href={store.authorization!.url}>{language.t("provider.connect.oauth.code.visit.link")}</Link>
<ExternalLink href={store.authorization!.url}>
{language.t("provider.connect.oauth.code.visit.link")}
</ExternalLink>
{language.t("provider.connect.oauth.code.visit.suffix", { provider: provider().name })}
</div>
<form onSubmit={handleSubmit} class="flex flex-col items-start gap-4">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unknown property 'class' found, use 'className' instead


React components use JSX, not HTML.
So we need to use JSX attributes and React replicate the respective HTML property/attribute while rendering.
Use of HTML property in JSX can sometimes lead to errors.
For example, class is a keyword in JavaScript (JSX is an extension of JavaScript), so it will throw an error.
However, in HTML it is a valid attribute.

Note: If you use React with Web Components, use the class attribute instead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is SolidJS, not React; Solid JSX uses the class attribute, so className would be incorrect here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unknown property 'class' found, use 'className' instead


React components use JSX, not HTML.
So we need to use JSX attributes and React replicate the respective HTML property/attribute while rendering.
Use of HTML property in JSX can sometimes lead to errors.
For example, class is a keyword in JavaScript (JSX is an extension of JavaScript), so it will throw an error.
However, in HTML it is a valid attribute.

Note: If you use React with Web Components, use the class attribute instead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate of 3691491541; SolidJS uses the class attribute, so className would be incorrect.

Expand Down Expand Up @@ -1077,7 +1079,9 @@ function ProviderConnection(props: {
<div class="flex flex-col gap-6">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unknown property 'class' found, use 'className' instead


React components use JSX, not HTML.
So we need to use JSX attributes and React replicate the respective HTML property/attribute while rendering.
Use of HTML property in JSX can sometimes lead to errors.
For example, class is a keyword in JavaScript (JSX is an extension of JavaScript), so it will throw an error.
However, in HTML it is a valid attribute.

Note: If you use React with Web Components, use the class attribute instead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is SolidJS, not React; Solid JSX uses the class attribute, so className would be incorrect here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unknown property 'class' found, use 'className' instead


React components use JSX, not HTML.
So we need to use JSX attributes and React replicate the respective HTML property/attribute while rendering.
Use of HTML property in JSX can sometimes lead to errors.
For example, class is a keyword in JavaScript (JSX is an extension of JavaScript), so it will throw an error.
However, in HTML it is a valid attribute.

Note: If you use React with Web Components, use the class attribute instead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate of 3691491555; SolidJS uses the class attribute, so className would be incorrect.

<div class="text-14-regular text-text-base">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unknown property 'class' found, use 'className' instead


React components use JSX, not HTML.
So we need to use JSX attributes and React replicate the respective HTML property/attribute while rendering.
Use of HTML property in JSX can sometimes lead to errors.
For example, class is a keyword in JavaScript (JSX is an extension of JavaScript), so it will throw an error.
However, in HTML it is a valid attribute.

Note: If you use React with Web Components, use the class attribute instead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is SolidJS, not React; Solid JSX uses the class attribute, so className would be incorrect here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unknown property 'class' found, use 'className' instead


React components use JSX, not HTML.
So we need to use JSX attributes and React replicate the respective HTML property/attribute while rendering.
Use of HTML property in JSX can sometimes lead to errors.
For example, class is a keyword in JavaScript (JSX is an extension of JavaScript), so it will throw an error.
However, in HTML it is a valid attribute.

Note: If you use React with Web Components, use the class attribute instead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate of 3691491562; SolidJS uses the class attribute, so className would be incorrect.

{language.t("provider.connect.oauth.auto.visit.prefix")}
<Link href={store.authorization!.url}>{language.t("provider.connect.oauth.auto.visit.link")}</Link>
<ExternalLink href={store.authorization!.url}>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forbidden non-null assertion


Using non-null assertions cancels out the benefits of strict null-checking, and introduces the possibility of runtime errors. Avoid non-null assertions unless absolutely necessary. If you still need to use one, write a skipcq comment to explain why it is safe.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

store.authorization is guaranteed set before this OAuth flow branch renders, and this is upstream-synced code on a read-only head branch.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forbidden non-null assertion


Using non-null assertions cancels out the benefits of strict null-checking, and introduces the possibility of runtime errors. Avoid non-null assertions unless absolutely necessary. If you still need to use one, write a skipcq comment to explain why it is safe.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate of 3691491564; the assertion is guarded by the OAuth flow state and the code is upstream-synced on a read-only head branch.

{language.t("provider.connect.oauth.auto.visit.link")}
</ExternalLink>
{language.t("provider.connect.oauth.auto.visit.suffix", { provider: provider().name })}
</div>
<TextField
Expand Down
6 changes: 3 additions & 3 deletions packages/app/src/components/dialog-custom-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { TextField } from "@opencode-ai/ui/text-field"
import { showToast } from "@/utils/toast"
import { batch, For } from "solid-js"
import { createStore, produce } from "solid-js/store"
import { Link } from "@/components/link"
import { ExternalLink } from "@/components/external-link"
import { useServerSDK } from "@/context/server-sdk"
import { useServerSync } from "@/context/server-sync"
import { useLanguage } from "@/context/language"
Expand Down Expand Up @@ -185,9 +185,9 @@ export function CustomProviderForm(props: { autofocus?: boolean } = {}) {
<form onSubmit={save} class="px-2.5 pb-6 flex flex-col gap-6">
<p class="text-14-regular text-text-base">
{language.t("provider.custom.description.prefix")}
<Link href="https://opencode.ai/docs/providers/#custom-provider" tabIndex={-1}>
<ExternalLink href="https://opencode.ai/docs/providers/#custom-provider" tabIndex={-1}>
{language.t("provider.custom.description.link")}
</Link>
</ExternalLink>
{language.t("provider.custom.description.suffix")}
</p>

Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/components/dialog-usage-exceeded.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function DialogUsageExceeded(props: DialogGoUpsellProps) {
const platform = usePlatform()

const runAction = () => {
if (props.link) platform.openLink(props.link)
if (props.link) platform.openExternal(props.link)
props.onClose?.()
dialog.close()
}
Expand Down
21 changes: 21 additions & 0 deletions packages/app/src/components/external-link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ComponentProps, splitProps } from "solid-js"

export interface ExternalLinkProps extends Omit<ComponentProps<"a">, "href"> {
href: string
}

export function ExternalLink(props: ExternalLinkProps) {
const [local, rest] = splitProps(props, ["href", "children", "class", "target", "rel"])

return (
<a
href={local.href}
class={`text-text-strong underline ${local.class ?? ""}`}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unknown property 'class' found, use 'className' instead


React components use JSX, not HTML.
So we need to use JSX attributes and React replicate the respective HTML property/attribute while rendering.
Use of HTML property in JSX can sometimes lead to errors.
For example, class is a keyword in JavaScript (JSX is an extension of JavaScript), so it will throw an error.
However, in HTML it is a valid attribute.

Note: If you use React with Web Components, use the class attribute instead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is SolidJS, not React; Solid JSX uses the class attribute, so className would be incorrect here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unknown property 'class' found, use 'className' instead


React components use JSX, not HTML.
So we need to use JSX attributes and React replicate the respective HTML property/attribute while rendering.
Use of HTML property in JSX can sometimes lead to errors.
For example, class is a keyword in JavaScript (JSX is an extension of JavaScript), so it will throw an error.
However, in HTML it is a valid attribute.

Note: If you use React with Web Components, use the class attribute instead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate of 3691491572; SolidJS uses the class attribute, so className would be incorrect.

target={local.target ?? "_blank"}
rel={local.rel ?? "noopener noreferrer"}
{...rest}
>
{local.children}
</a>
)
}
Comment on lines +7 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function is declared at ES module scope and exported, not in the global scope; this browser-script rule does not apply to modules.

Comment on lines +7 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate of 3691491569; module-scope exported function, not a global-scope declaration.

26 changes: 0 additions & 26 deletions packages/app/src/components/link.tsx

This file was deleted.

4 changes: 2 additions & 2 deletions packages/app/src/components/settings-general.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
} from "@/context/settings"
import { decode64 } from "@/utils/base64"
import { playSoundById, SOUND_OPTIONS } from "@/utils/sound"
import { Link } from "./link"
import { ExternalLink } from "./external-link"
import { SettingsList } from "./settings-list"

let demoSoundState = {
Expand Down Expand Up @@ -482,7 +482,7 @@ export const SettingsGeneral: Component = () => {
description={
<>
{language.t("settings.general.row.theme.description")}{" "}
<Link href="https://opencode.ai/docs/themes/">{language.t("common.learnMore")}</Link>
<ExternalLink href="https://opencode.ai/docs/themes/">{language.t("common.learnMore")}</ExternalLink>
</>
}
>
Expand Down
6 changes: 3 additions & 3 deletions packages/app/src/components/settings-v2/general.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useLanguage } from "@/context/language"
import { usePlatform } from "@/context/platform"
import { useUpdaterAction } from "../updater-action"
import { useSettings } from "@/context/settings"
import { Link } from "../link"
import { ExternalLink } from "../external-link"
import { SettingsListV2 } from "./parts/list"
import { SettingsRowV2 } from "./parts/row"
import { LayoutRetirementNotice, LayoutTransitionToggle } from "./interface-transition"
Expand Down Expand Up @@ -150,9 +150,9 @@ const AppearanceSection: Component<{ controller: AppearanceSettingsController }>
description={
<>
{language.t("settings.general.row.theme.description")}{" "}
<Link class="settings-v2-link" href="https://opencode.ai/docs/themes/">
<ExternalLink class="settings-v2-link" href="https://opencode.ai/docs/themes/">
{language.t("common.learnMore")}
</Link>
</ExternalLink>
</>
}
>
Expand Down
6 changes: 5 additions & 1 deletion packages/app/src/components/terminal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,11 @@ export const Terminal = (props: TerminalProps) => {

event.preventDefault()
event.stopImmediatePropagation()
platform.openLink(text)
if (URL.canParse(text) && new URL(text).protocol === "file:" && platform.openLocalFile) {
platform.openLocalFile(text)
return
}
platform.openExternal(text)
}

onMount(() => {
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/components/windows-app-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function WindowsAppMenu(props: {
runAction(entry.action)
return
}
if (entry.href) props.platform.openLink(entry.href)
if (entry.href) props.platform.openExternal(entry.href)
}

return (
Expand Down
11 changes: 8 additions & 3 deletions packages/app/src/context/notification.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createStore, reconcile } from "solid-js/store"
import { type Accessor, batch, createEffect, createMemo, createRoot, getOwner, onCleanup } from "solid-js"
import { useParams, useSearchParams } from "@solidjs/router"
import { useNavigate, useParams, useSearchParams } from "@solidjs/router"
import { createSimpleContext } from "@opencode-ai/ui/context"
import type { ServerSDK } from "./server-sdk"
import type { ServerSync } from "./server-sync"
Expand Down Expand Up @@ -118,6 +118,7 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
const global = useGlobal()
const server = useServer()
const tabs = useTabs()
const navigate = useNavigate()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Hook "useNavigate" is called in function "init" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"


Rule 1: Only Call hooks at the Top Level. Don't call hooks inside loops, conditions, or nested functions. Instead, always use hooks at the top level of your React function.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useNavigate is from @solidjs/router, called inside a Solid context factory, not a React hook; React hooks rules do not apply to this codebase.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Hook "useNavigate" is called in function "init" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"


Rule 1: Only Call hooks at the Top Level. Don't call hooks inside loops, conditions, or nested functions. Instead, always use hooks at the top level of your React function.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate of 3691491579; Solid router primitive, not a React hook.

const platform = usePlatform()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Hook "usePlatform" is called in function "init" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"


Rule 1: Only Call hooks at the Top Level. Don't call hooks inside loops, conditions, or nested functions. Instead, always use hooks at the top level of your React function.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

usePlatform is a SolidJS context accessor called inside a Solid context factory, not a React hook; React hooks rules do not apply to this codebase.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Hook "usePlatform" is called in function "init" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"


Rule 1: Only Call hooks at the Top Level. Don't call hooks inside loops, conditions, or nested functions. Instead, always use hooks at the top level of your React function.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate of 3691491584; Solid context accessor, not a React hook.

const settings = useSettings()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Hook "useSettings" is called in function "init" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"


Rule 1: Only Call hooks at the Top Level. Don't call hooks inside loops, conditions, or nested functions. Instead, always use hooks at the top level of your React function.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useSettings is a SolidJS context accessor called inside a Solid context factory, not a React hook; React hooks rules do not apply to this codebase.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Hook "useSettings" is called in function "init" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"


Rule 1: Only Call hooks at the Top Level. Don't call hooks inside loops, conditions, or nested functions. Instead, always use hooks at the top level of your React function.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate of 3691491587; Solid context accessor, not a React hook.

const language = useLanguage()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Hook "useLanguage" is called in function "init" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"


Rule 1: Only Call hooks at the Top Level. Don't call hooks inside loops, conditions, or nested functions. Instead, always use hooks at the top level of your React function.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useLanguage is a SolidJS context accessor called inside a Solid context factory, not a React hook; React hooks rules do not apply to this codebase.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Hook "useLanguage" is called in function "init" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"


Rule 1: Only Call hooks at the Top Level. Don't call hooks inside loops, conditions, or nested functions. Instead, always use hooks at the top level of your React function.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate of 3691491595; Solid context accessor, not a React hook.

Expand Down Expand Up @@ -153,6 +154,7 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
platform,
settings,
language,
navigate,
}),
}),
owner ?? undefined,
Expand Down Expand Up @@ -217,6 +219,7 @@ function createServerNotificationState(input: {
platform: ReturnType<typeof usePlatform>
settings: ReturnType<typeof useSettings>
language: ReturnType<typeof useLanguage>
navigate: (href: string) => void
}) {
const serverSDK = () => input.sdk
const serverSync = () => input.sync
Expand Down Expand Up @@ -353,7 +356,9 @@ function createServerNotificationState(input: {

const href = `/${base64Encode(directory)}/session/${sessionID}`
if (settings.notifications.agent()) {
void platform.notify(language.t("notification.session.responseReady.title"), session.title ?? sessionID, href)
void platform.notify(language.t("notification.session.responseReady.title"), session.title ?? sessionID, () =>
input.navigate(href),
)
Comment on lines +359 to +361

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected 'undefined' and instead saw 'void'


The void operator takes an operand and returns undefined. It can be used to ignore the value produced by an expression. However, this can lead to code that is difficult to understand and maintain. Historically, the void operator was used to get a "pure" undefined value, as the undefined variable was mutable prior to ES5.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

void here intentionally discards a fire-and-forget promise, an idiomatic pattern in this codebase; purely stylistic with no bug risk.

Comment on lines +359 to +361

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected 'undefined' and instead saw 'void'


The void operator takes an operand and returns undefined. It can be used to ignore the value produced by an expression. However, this can lead to code that is difficult to understand and maintain. Historically, the void operator was used to get a "pure" undefined value, as the undefined variable was mutable prior to ES5.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate of 3691491597; intentional fire-and-forget void, stylistic only.

}
})
}
Expand Down Expand Up @@ -386,7 +391,7 @@ function createServerNotificationState(input: {
(typeof error === "string" ? error : language.t("notification.session.error.fallbackDescription"))
const href = sessionID ? `/${base64Encode(directory)}/session/${sessionID}` : `/${base64Encode(directory)}`
if (settings.notifications.errors()) {
void platform.notify(language.t("notification.session.error.title"), description, href)
void platform.notify(language.t("notification.session.error.title"), description, () => input.navigate(href))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected 'undefined' and instead saw 'void'


The void operator takes an operand and returns undefined. It can be used to ignore the value produced by an expression. However, this can lead to code that is difficult to understand and maintain. Historically, the void operator was used to get a "pure" undefined value, as the undefined variable was mutable prior to ES5.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

void here intentionally discards a fire-and-forget promise, an idiomatic pattern in this codebase; purely stylistic with no bug risk.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected 'undefined' and instead saw 'void'


The void operator takes an operand and returns undefined. It can be used to ignore the value produced by an expression. However, this can lead to code that is difficult to understand and maintain. Historically, the void operator was used to get a "pure" undefined value, as the undefined variable was mutable prior to ES5.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate of 3691491604; intentional fire-and-forget void, stylistic only.

}
})
}
Expand Down
20 changes: 7 additions & 13 deletions packages/app/src/context/platform.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,23 @@ type PlatformBase = {
/** App version */
version?: string

/** Open a URL in the default browser */
openLink(url: string): void
/** Open a web or mail URL in the default system application */
openExternal(url: string): void

/** Open a local path in a local app (desktop only) */
openPath?(path: string, app?: string): Promise<void>

/** Open a local file URL in its default app (desktop only) */
openLocalFile?(url: string): void

/** Reveal a local path in the system file manager; false when the path does not exist (desktop only) */
revealPath?(path: string): Promise<boolean>

/** Restart the app */
restart(): Promise<void>

/** Navigate back in history */
back(): void

/** Navigate forward in history */
forward(): void

/** Send a system notification (optional deep link) */
notify(title: string, description?: string, href?: string): Promise<void>
/** Send a system notification */
notify(title: string, description?: string, onClick?: () => void): Promise<void>

/** Open a native attachment picker and read selected files sequentially (desktop only) */
openAttachmentPickerDialog?(
Expand Down Expand Up @@ -91,9 +88,6 @@ type PlatformBase = {
/** Set the preferred display backend (desktop only) */
setDisplayBackend?(backend: DisplayBackend): Promise<void>

/** Parse markdown to HTML using native parser (desktop only, returns unprocessed code blocks) */
parseMarkdown?(markdown: string): Promise<string>

/** Webview zoom level (desktop only) */
webviewZoom?: Accessor<number>

Expand Down
Loading
Loading