Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 14 additions & 8 deletions windows/tauri/src/extensions/hooks/use-extension-install-prompt.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useRef } from "react";
import { useBufferStore } from "@/features/editor/stores/buffer.store";
import { useToast } from "@/features/layout/contexts/toast-context";
import { useTranslation } from "@/i18n/locale-provider";
import { useExtensionStore } from "../registry/extension-store";

interface ExtensionInstallNeededEvent {
Expand All @@ -13,6 +14,7 @@ interface ExtensionInstallNeededEvent {
const activePrompts = new Map<string, string>();

export const useExtensionInstallPrompt = () => {
const { t } = useTranslation();
const { showToast, dismissToast, updateToast, hasToast } = useToast();
const { installExtension } = useExtensionStore.use.actions();
const dismissedExtensions = useRef<Set<string>>(new Set());
Expand Down Expand Up @@ -40,16 +42,16 @@ export const useExtensionInstallPrompt = () => {
}

const toastId = showToast({
message: `${extensionName} extension not installed. Install it to enable language support?`,
message: t("extensions.installPrompt", { name: extensionName }),
type: "info",
duration: 0, // Don't auto-dismiss
action: {
label: "Install",
label: t("extensions.install"),
onClick: async () => {
try {
// Update toast to show installing status
updateToast(toastId, {
message: `Installing ${extensionName}...`,
message: t("extensions.installing", { name: extensionName }),
action: undefined, // Remove action button while installing
});

Expand All @@ -58,7 +60,7 @@ export const useExtensionInstallPrompt = () => {

// Show success
updateToast(toastId, {
message: `${extensionName} installed successfully!`,
message: t("extensions.installSuccess", { name: extensionName }),
type: "success",
});

Expand All @@ -81,14 +83,18 @@ export const useExtensionInstallPrompt = () => {
}, 3000);
} catch (error) {
// Show error
const errorMessage = error instanceof Error ? error.message : "Installation failed";
const errorMessage =
error instanceof Error ? error.message : t("extensions.installFailedGeneric");
console.error(`Failed to install ${extensionName}:`, error);

updateToast(toastId, {
message: `Failed to install ${extensionName}: ${errorMessage}`,
message: t("extensions.installFailedWithMessage", {
name: extensionName,
message: errorMessage,
}),
type: "error",
action: {
label: "Retry",
label: t("ui.retry"),
onClick: () => {
// Retry installation
dismissToast(toastId);
Expand Down Expand Up @@ -131,5 +137,5 @@ export const useExtensionInstallPrompt = () => {
window.removeEventListener("extension-install-needed", handleInstallNeeded);
window.removeEventListener("toast-dismissed", handleToastDismiss);
};
}, [showToast, dismissToast, updateToast, installExtension, hasToast]);
}, [t, showToast, dismissToast, updateToast, installExtension, hasToast]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import DOMPurify from "dompurify";
import { cloneElement, isValidElement, useMemo, useSyncExternalStore } from "react";
import { themeRegistry } from "@/extensions/themes/theme-registry";
import { getDefaultSetting, useSettingsStore } from "@/features/settings/stores/settings.store";
import { useTranslation } from "@/i18n/locale-provider";
import { cn } from "@/utils/cn";
import { iconThemeRegistry } from "../icon-theme-registry";

Expand All @@ -27,6 +28,7 @@ export function ThemedFileIcon({
isSymlink = false,
className = "text-subtle-foreground",
}: ThemedFileIconProps) {
const { t } = useTranslation();
const iconThemeId = useSettingsStore((state) => state.settings.iconTheme);
useSyncExternalStore(
(callback) => iconThemeRegistry.onRegistryChange(callback),
Expand Down Expand Up @@ -95,9 +97,9 @@ export function ThemedFileIcon({
viewBox="0 0 16 16"
className="-bottom-0.5 -right-0.5 themed-file-icon-badge absolute text-primary"
role="img"
aria-label="Symlink"
aria-label={t("ui.symlink")}
>
<title>Symlink</title>
<title>{t("ui.symlink")}</title>
<path
fill="currentColor"
d="M6.879 9.934a.81.81 0 0 1-.575-.238 3.818 3.818 0 0 1 0-5.392l3-3C10.024.584 10.982.187 12 .187s1.976.397 2.696 1.117a3.818 3.818 0 0 1 0 5.392l-1.371 1.371a.813.813 0 0 1-1.149-1.149l1.371-1.371A2.19 2.19 0 0 0 12 1.812c-.584 0-1.134.228-1.547.641l-3 3a2.19 2.19 0 0 0 0 3.094.813.813 0 0 1-.575 1.387z"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { XIcon as X } from "@/ui/icons";
import { useUIExtensionStore } from "../stores/ui-extension-store";
import { ExtensionErrorBoundary } from "./extension-error-boundary";
import { useTranslation } from "@/i18n/locale-provider";
import { Button } from "@/ui/button";
import { Dialog, DialogClose, DialogContent, DialogHeader, DialogTitle } from "@/ui/dialog";
import { ScrollArea } from "@/ui/scroll-area";

export function ExtensionDialogs() {
const { t } = useTranslation();
const activeDialogs = useUIExtensionStore.use.activeDialogs();
const closeDialog = useUIExtensionStore.use.actions().closeDialog;

Expand Down Expand Up @@ -33,7 +35,9 @@ export function ExtensionDialogs() {
<DialogHeader className="flex-row items-center justify-between gap-2 border-border border-b px-4 py-3">
<DialogTitle>{dialog.title}</DialogTitle>
<DialogClose
render={<Button variant="ghost" size="icon-xs" aria-label="Close dialog" />}
render={
<Button variant="ghost" size="icon-xs" aria-label={t("ui.closeDialog")} />
}
>
<X />
</DialogClose>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Component, type ErrorInfo, type ReactNode } from "react";
import { WarningIcon as AlertTriangle } from "@/ui/icons";
import { useSettingsStore } from "@/features/settings/stores/settings.store";
import { createTranslator } from "@/i18n/locale";
import { Button } from "@/ui/button";
import {
Empty,
Expand Down Expand Up @@ -38,25 +40,27 @@ export class ExtensionErrorBoundary extends Component<Props, State> {

render() {
if (this.state.hasError) {
const t = createTranslator(useSettingsStore.getState().settings.displayLanguage);

return (
<Empty className="h-full rounded-none p-4" tone="warning" role="alert">
<EmptyHeader>
<EmptyMedia>
<AlertTriangle className="size-8" />
</EmptyMedia>
<EmptyTitle>{this.props.name} crashed</EmptyTitle>
<EmptyTitle>{t("extensions.crashed", { name: this.props.name })}</EmptyTitle>
<EmptyDescription>
{this.state.error?.message || "An unexpected error occurred"}
{this.state.error?.message || t("extensions.unexpectedError")}
</EmptyDescription>
</EmptyHeader>
<EmptyContent>
<Button
onClick={this.handleRetry}
variant="default"
aria-label={`Retry loading ${this.props.name}`}
aria-label={t("extensions.retryLoading", { name: this.props.name })}
size="xs"
>
Retry
{t("ui.retry")}
</Button>
</EmptyContent>
</Empty>
Expand Down
Loading
Loading