mirror of
https://github.com/neosubhamoy/neodlp-extension.git
synced 2025-12-20 01:49:34 +05:30
feat: added keyboard shortcut indication
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
### ⬇️ Download Section
|
### ⬇️ Download Section
|
||||||
|
|
||||||
| Type\Browser | Chrome / Chromium based | Firefox / Firefox based |
|
| Type | Chrome / Chromium base | Firefox base |
|
||||||
| :---- | :---- | :---- |
|
| :---- | :---- | :---- |
|
||||||
| Store Listed (Recommended - Auto updates) | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
|
| Store Listed (Recommended - Auto updates) | [Get](https://chromewebstore.google.com/detail/neo-downloader-plus/mehopeailfjmiloiiohgicphlcgpompf) | [Get](https://addons.mozilla.org/en-US/firefox/addon/neo-downloader-plus) |
|
||||||
| Unpackable ZIP (Manual updates) | [Download](https://github.com/neosubhamoy/neodlp-extension/releases/download/<release_tag>/neodlp-extension-<version>-chrome.zip) | [Download](https://github.com/neosubhamoy/neodlp-extension/releases/download/<release_tag>/neodlp-extension-<version>-firefox.zip) |
|
| Unpackable ZIP (Manual updates) | [Download](https://github.com/neosubhamoy/neodlp-extension/releases/download/<release_tag>/neodlp-extension-<version>-chrome.zip) | [Download](https://github.com/neosubhamoy/neodlp-extension/releases/download/<release_tag>/neodlp-extension-<version>-firefox.zip) |
|
||||||
@@ -16,7 +16,7 @@ NeoDLP (Neo Downloader Plus) Browser Integration
|
|||||||
|
|
||||||
#### Direct Installation from Official Web Store Listing
|
#### Direct Installation from Official Web Store Listing
|
||||||
|
|
||||||
[link-chrome]: https://chromewebstore.google.com/detail/plex-skipper/mehopeailfjmiloiiohgicphlcgpompf "Version published on Chrome Web Store"
|
[link-chrome]: https://chromewebstore.google.com/detail/neo-downloader-plus/mehopeailfjmiloiiohgicphlcgpompf "Version published on Chrome Web Store"
|
||||||
[link-firefox]: https://addons.mozilla.org/en-US/firefox/addon/neo-downloader-plus/ "Version published on Mozilla Add-ons"
|
[link-firefox]: https://addons.mozilla.org/en-US/firefox/addon/neo-downloader-plus/ "Version published on Mozilla Add-ons"
|
||||||
|
|
||||||
[<img src="https://raw.githubusercontent.com/alrra/browser-logos/90fdf03c/src/chrome/chrome.svg" width="48" alt="Chrome" valign="middle">][link-chrome] [<img valign="middle" src="https://img.shields.io/chrome-web-store/v/mehopeailfjmiloiiohgicphlcgpompf.svg?label=%20">][link-chrome] also compatible with [<img src="https://raw.githubusercontent.com/alrra/browser-logos/90fdf03c/src/edge/edge.svg" width="24" alt="Edge" valign="middle">][link-chrome] [<img src="https://raw.githubusercontent.com/alrra/browser-logos/90fdf03c/src/opera/opera.svg" width="24" alt="Opera" valign="middle">][link-chrome]
|
[<img src="https://raw.githubusercontent.com/alrra/browser-logos/90fdf03c/src/chrome/chrome.svg" width="48" alt="Chrome" valign="middle">][link-chrome] [<img valign="middle" src="https://img.shields.io/chrome-web-store/v/mehopeailfjmiloiiohgicphlcgpompf.svg?label=%20">][link-chrome] also compatible with [<img src="https://raw.githubusercontent.com/alrra/browser-logos/90fdf03c/src/edge/edge.svg" width="24" alt="Edge" valign="middle">][link-chrome] [<img src="https://raw.githubusercontent.com/alrra/browser-logos/90fdf03c/src/opera/opera.svg" width="24" alt="Opera" valign="middle">][link-chrome]
|
||||||
|
|||||||
@@ -9,14 +9,13 @@ const buttonVariants = cva(
|
|||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default:
|
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||||
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
||||||
destructive:
|
destructive:
|
||||||
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||||
outline:
|
outline:
|
||||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||||
secondary:
|
secondary:
|
||||||
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||||
ghost:
|
ghost:
|
||||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||||
link: "text-primary underline-offset-4 hover:underline",
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
@@ -26,6 +25,8 @@ const buttonVariants = cva(
|
|||||||
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||||
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||||
icon: "size-9",
|
icon: "size-9",
|
||||||
|
"icon-sm": "size-8",
|
||||||
|
"icon-lg": "size-10",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
<div
|
<div
|
||||||
data-slot="card-header"
|
data-slot="card-header"
|
||||||
className={cn(
|
className={cn(
|
||||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
import { Slot } from "@radix-ui/react-slot"
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
|
|||||||
28
components/ui/kbd.tsx
Normal file
28
components/ui/kbd.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
|
||||||
|
return (
|
||||||
|
<kbd
|
||||||
|
data-slot="kbd"
|
||||||
|
className={cn(
|
||||||
|
"bg-muted text-muted-foreground pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm px-1 font-sans text-xs font-medium select-none",
|
||||||
|
"[&_svg:not([class*='size-'])]:size-3",
|
||||||
|
"[[data-slot=tooltip-content]_&]:bg-background/20 [[data-slot=tooltip-content]_&]:text-background dark:[[data-slot=tooltip-content]_&]:bg-background/10",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<kbd
|
||||||
|
data-slot="kbd-group"
|
||||||
|
className={cn("inline-flex items-center gap-1", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Kbd, KbdGroup }
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
||||||
|
|
||||||
|
|||||||
2421
package-lock.json
generated
2421
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
42
package.json
42
package.json
@@ -15,30 +15,30 @@
|
|||||||
"postinstall": "wxt prepare"
|
"postinstall": "wxt prepare"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^5.2.0",
|
"@hookform/resolvers": "^5.2.2",
|
||||||
"@radix-ui/react-label": "^2.1.7",
|
"@radix-ui/react-label": "^2.1.8",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
"@radix-ui/react-switch": "^1.2.5",
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
"@radix-ui/react-tabs": "^1.1.12",
|
"@radix-ui/react-tabs": "^1.1.13",
|
||||||
"@tailwindcss/vite": "^4.1.11",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.526.0",
|
"lucide-react": "^0.553.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.2.0",
|
||||||
"react-hook-form": "^7.61.1",
|
"react-hook-form": "^7.66.0",
|
||||||
"react-router-dom": "^7.7.1",
|
"react-router-dom": "^7.9.5",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.4.0",
|
||||||
"tailwindcss": "^4.1.11",
|
"tailwindcss": "^4.1.17",
|
||||||
"tw-animate-css": "^1.3.6",
|
"tw-animate-css": "^1.4.0",
|
||||||
"zod": "^4.0.10"
|
"zod": "^4.1.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.1.0",
|
"@types/node": "^24.10.0",
|
||||||
"@types/react": "^19.1.8",
|
"@types/react": "^19.2.2",
|
||||||
"@types/react-dom": "^19.1.6",
|
"@types/react-dom": "^19.2.2",
|
||||||
"@wxt-dev/module-react": "^1.1.3",
|
"@wxt-dev/module-react": "^1.1.5",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.9.3",
|
||||||
"wxt": "^0.20.7"
|
"wxt": "^0.20.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ import { Settings } from "@/types/settings";
|
|||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
import { formatKeySymbol } from "@/utils";
|
||||||
import { useTheme } from "@/components/theme-provider";
|
import { useTheme } from "@/components/theme-provider";
|
||||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||||
|
import { Kbd, KbdGroup } from "@/components/ui/kbd";
|
||||||
|
|
||||||
const downloadFormSchema = z.object({
|
const downloadFormSchema = z.object({
|
||||||
url: z.url({
|
url: z.url({
|
||||||
@@ -32,6 +34,7 @@ export default function HomePage() {
|
|||||||
theme: "system",
|
theme: "system",
|
||||||
autofill_url: true,
|
autofill_url: true,
|
||||||
});
|
});
|
||||||
|
const [shortcuts, setShortcuts] = useState<Browser.commands.Command[]>([]);
|
||||||
|
|
||||||
const themeOptions: { value: 'system' | 'dark' | 'light'; icon: LucideIcon; label: string }[] = [
|
const themeOptions: { value: 'system' | 'dark' | 'light'; icon: LucideIcon; label: string }[] = [
|
||||||
{ value: 'light', icon: Sun, label: 'Light' },
|
{ value: 'light', icon: Sun, label: 'Light' },
|
||||||
@@ -130,6 +133,13 @@ export default function HomePage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Fetch all Commands when the component mounts
|
||||||
|
useEffect(() => {
|
||||||
|
browser.commands.getAll().then(commands => {
|
||||||
|
setShortcuts(commands);
|
||||||
|
}).catch(console.error);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// loading the settings from storage if available, overwriting the default values when the component mounts
|
// loading the settings from storage if available, overwriting the default values when the component mounts
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadSettings = async () => {
|
const loadSettings = async () => {
|
||||||
@@ -173,7 +183,7 @@ export default function HomePage() {
|
|||||||
// Listen for tab URL changes and update the form value accordingly (if autofill is enabled)
|
// Listen for tab URL changes and update the form value accordingly (if autofill is enabled)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isLoadingSettings || !settings.autofill_url) return;
|
if (isLoadingSettings || !settings.autofill_url) return;
|
||||||
const handleTabUrlChange = async (tabId: number, changeInfo: Browser.tabs.TabChangeInfo) => {
|
const handleTabUrlChange = async (tabId: number, changeInfo: Browser.tabs.OnUpdatedInfo) => {
|
||||||
if (changeInfo.status === "complete") {
|
if (changeInfo.status === "complete") {
|
||||||
browser.tabs.get(tabId).then(async (tab) => {
|
browser.tabs.get(tabId).then(async (tab) => {
|
||||||
if (tab.active && tab.url) {
|
if (tab.active && tab.url) {
|
||||||
@@ -270,6 +280,29 @@ export default function HomePage() {
|
|||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
<div className="or-devider after:border-border relative text-center text-xs after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t mb-2">
|
||||||
|
<span className="bg-background text-muted-foreground relative z-10 px-2">
|
||||||
|
OR
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="quick-download-suggestion flex flex-col items-center justify-center space-y-2">
|
||||||
|
<p className="text-sm flex items-center gap-2"><span className="">Use, Shortcut</span>
|
||||||
|
{
|
||||||
|
shortcuts.find(cmd => cmd.name === "neodlp:quick-download")?.shortcut ? (
|
||||||
|
<KbdGroup>
|
||||||
|
{shortcuts.find(cmd => cmd.name === "neodlp:quick-download")?.shortcut?.split('+').map((key, index, arr) => (
|
||||||
|
<span key={index} className="flex items-center">
|
||||||
|
<Kbd>{formatKeySymbol(key.trim())}</Kbd>
|
||||||
|
{index < arr.length - 1 && <span className="ml-1">+</span>}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</KbdGroup>
|
||||||
|
) : (
|
||||||
|
<Kbd>⚠️ Not Set</Kbd>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./*"]
|
"@/*": ["./*"]
|
||||||
}
|
}
|
||||||
|
|||||||
1
types/os.ts
Normal file
1
types/os.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export type OS = 'windows' | 'macos' | 'linux' | 'android' | 'ios' | 'unknown';
|
||||||
39
utils.ts
Normal file
39
utils.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { OS } from "@/types/os";
|
||||||
|
|
||||||
|
export function getOS(): OS {
|
||||||
|
const userAgent = window.navigator.userAgent;
|
||||||
|
const platform = (window.navigator as any)?.userAgentData?.platform || window.navigator.platform;
|
||||||
|
|
||||||
|
const macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K', 'macOS'];
|
||||||
|
const windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'];
|
||||||
|
const iosPlatforms = ['iPhone', 'iPad', 'iPod'];
|
||||||
|
|
||||||
|
if (macosPlatforms.includes(platform)) {
|
||||||
|
return 'macos';
|
||||||
|
} else if (iosPlatforms.includes(platform)) {
|
||||||
|
return 'ios';
|
||||||
|
} else if (windowsPlatforms.includes(platform)) {
|
||||||
|
return 'windows';
|
||||||
|
} else if (/Android/.test(userAgent)) {
|
||||||
|
return 'android';
|
||||||
|
} else if (/Linux/.test(platform)) {
|
||||||
|
return 'linux';
|
||||||
|
} else {
|
||||||
|
return 'unknown';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatKeySymbol(key: string): string {
|
||||||
|
switch (key.toLowerCase()) {
|
||||||
|
case 'option':
|
||||||
|
return '⌥';
|
||||||
|
case 'shift':
|
||||||
|
return '⇧';
|
||||||
|
case 'command':
|
||||||
|
return '⌘';
|
||||||
|
case 'macctrl':
|
||||||
|
return '⌃';
|
||||||
|
default:
|
||||||
|
return key.toUpperCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user