mirror of
https://github.com/neosubhamoy/neodlp-extension.git
synced 2025-12-20 04:09:34 +05:30
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
4da7838d70
|
|||
|
093ea4ebd1
|
|||
|
8f20617d16
|
|||
|
cea4fcefae
|
|||
|
a2c29e87fd
|
|||
|
1dba1bdb8c
|
|||
|
df65949e63
|
|||
|
aa4cbc0a84
|
|||
|
ac18d04f8e
|
39
.github/workflows/publish.yml
vendored
Normal file
39
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
name: 🚀 Publish to Stores
|
||||||
|
jobs:
|
||||||
|
submit:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: 🚚 Checkout repository
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: 📦 Setup node
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 'lts/*'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: 🛠️ Install dependencies
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: 🤐 Zip extensions
|
||||||
|
run: |
|
||||||
|
npm run zip
|
||||||
|
npm run zip:firefox
|
||||||
|
|
||||||
|
- name: 🚀 Submit to stores
|
||||||
|
run: |
|
||||||
|
npm run submit -- --chrome-zip .output/*-chrome.zip \
|
||||||
|
--firefox-zip .output/*-firefox.zip --firefox-sources-zip .output/*-sources.zip
|
||||||
|
env:
|
||||||
|
CHROME_EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}
|
||||||
|
CHROME_CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
|
||||||
|
CHROME_CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
|
||||||
|
CHROME_REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}
|
||||||
|
CHROME_SKIP_SUBMIT_REVIEW: true
|
||||||
|
FIREFOX_EXTENSION_ID: ${{ secrets.FIREFOX_EXTENSION_ID }}
|
||||||
|
FIREFOX_JWT_ISSUER: ${{ secrets.FIREFOX_JWT_ISSUER }}
|
||||||
|
FIREFOX_JWT_SECRET: ${{ secrets.FIREFOX_JWT_SECRET }}
|
||||||
7
.github/workflows/release.yml
vendored
7
.github/workflows/release.yml
vendored
@@ -9,10 +9,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: 🚚 Checkout repository
|
- name: 🚚 Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: 📦 Setup node
|
- name: 📦 Setup node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
@@ -82,6 +82,5 @@ jobs:
|
|||||||
.output/${{github.event.repository.name}}-${{env.version}}-firefox.zip
|
.output/${{github.event.repository.name}}-${{env.version}}-firefox.zip
|
||||||
.output/${{github.event.repository.name}}-${{env.version}}-sources.zip
|
.output/${{github.event.repository.name}}-${{env.version}}-sources.zip
|
||||||
latest.json
|
latest.json
|
||||||
draft: false
|
draft: true
|
||||||
prerelease: false
|
prerelease: false
|
||||||
make_latest: true
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
### ✨ Changelog
|
### ✨ Changelog
|
||||||
|
|
||||||
- initial MVP release v0.1.0
|
- Added keyboard shortcut indication
|
||||||
|
- Quick download is now renamed to quick search
|
||||||
|
- Other minor fixes and improvements
|
||||||
|
|
||||||
### ⬇️ 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) |
|
||||||
26
README.md
26
README.md
@@ -6,15 +6,25 @@ NeoDLP (Neo Downloader Plus) Browser Integration
|
|||||||
[](https://github.com/neosubhamoy/neodlp-extension)
|
[](https://github.com/neosubhamoy/neodlp-extension)
|
||||||
[](https://github.com/neosubhamoy/neodlp-extension)
|
[](https://github.com/neosubhamoy/neodlp-extension)
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
> **🥰 Liked this project? Please consider giving it a Star (🌟) on github to show us your appreciation and help the algorythm recommend this project to even more awesome people like you!**
|
> **🥰 Liked this project? Please consider giving it a Star (🌟) on github to show us your appreciation and help the algorythm recommend this project to even more awesome people like you!**
|
||||||
|
|
||||||
### 📎 Pre-Requirements
|
## 📎 Pre-Requirements
|
||||||
|
|
||||||
- [NeoDLP](https://github.com/neosubhamoy/neodlp) (Installed and Running)
|
- [NeoDLP](https://github.com/neosubhamoy/neodlp) (Installed and Running)
|
||||||
|
|
||||||
### ⬇️ Download and Installation
|
## ⬇️ Download and Installation
|
||||||
|
|
||||||
#### Manual Installation (in Google Chrome / Chromium based browsers)
|
### Direct Installation from Official Store Listing
|
||||||
|
|
||||||
|
[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"
|
||||||
|
|
||||||
|
[<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/firefox/firefox.svg" width="48" alt="Firefox" valign="middle">][link-firefox] [<img valign="middle" src="https://img.shields.io/amo/v/neo-downloader-plus.svg?label=%20">][link-firefox]
|
||||||
|
|
||||||
|
### Manual Installation (in Google Chrome / Chromium based browsers)
|
||||||
|
|
||||||
1. Download the `neodlp-extension-<version>-chrome.zip` file from the [latest release](https://github.com/neosubhamoy/neodlp-extension/releases).
|
1. Download the `neodlp-extension-<version>-chrome.zip` file from the [latest release](https://github.com/neosubhamoy/neodlp-extension/releases).
|
||||||
2. Extract the `.zip` file (using any zip extractor software. eg: 7zip, WinRAR etc.).
|
2. Extract the `.zip` file (using any zip extractor software. eg: 7zip, WinRAR etc.).
|
||||||
@@ -22,21 +32,21 @@ NeoDLP (Neo Downloader Plus) Browser Integration
|
|||||||
4. Enable "Developer mode" in the top right corner.
|
4. Enable "Developer mode" in the top right corner.
|
||||||
5. Click on "Load unpacked" and select the newly extracted folder. Done! That's it...!!
|
5. Click on "Load unpacked" and select the newly extracted folder. Done! That's it...!!
|
||||||
|
|
||||||
#### Manual Installation (in Mozilla Firefox)
|
### Manual Installation (in Mozilla Firefox)
|
||||||
|
|
||||||
1. Download the `neodlp-extension-<version>-firefox.zip` file from the [latest release](https://github.com/neosubhamoy/neodlp-extension/releases).
|
1. Download the `neodlp-extension-<version>-firefox.zip` file from the [latest release](https://github.com/neosubhamoy/neodlp-extension/releases).
|
||||||
2. Open Firefox and navigate to `about:addons`.
|
2. Open Firefox and navigate to `about:addons`.
|
||||||
3. Click on the gear icon (top right) and select "Install Add-on From File...".
|
3. Click on the gear icon (top right) and select "Install Add-on From File...".
|
||||||
4. Select the downloaded `.zip` file. Done! That's it...!!
|
4. Select the downloaded `.zip` file. Done! That's it...!!
|
||||||
|
|
||||||
### ⚡ Technologies Used
|
## ⚡ Technologies Used
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
### 🛠️ Contributing / Building from Source
|
## 🛠️ Contributing / Building from Source
|
||||||
|
|
||||||
Want to be part of this? Feel free to contribute...!! Pull Requests are always welcome...!! (^_^) Follow these simple steps to start building:
|
Want to be part of this? Feel free to contribute...!! Pull Requests are always welcome...!! (^_^) Follow these simple steps to start building:
|
||||||
|
|
||||||
@@ -55,8 +65,8 @@ npm run zip:firefox # for production zip creation (firefox)
|
|||||||
```
|
```
|
||||||
5. Do the changes, Send a Pull Request with proper Description (NOTE: Pull Requests Without Proper Description will be Rejected)
|
5. Do the changes, Send a Pull Request with proper Description (NOTE: Pull Requests Without Proper Description will be Rejected)
|
||||||
|
|
||||||
**⭕ Noticed any Bugs or Want to give us some suggetions? Always feel free to open a GitHub Issue. We would love to hear from you...!!**
|
**⭕ Noticed any Bug or Want to give us some suggetions? Always feel free to open a GitHub Issue. We would love to hear from you...!!**
|
||||||
|
|
||||||
### 📝 License
|
## 📝 License
|
||||||
|
|
||||||
NeoDLP Extension is Licensed under the [MIT license](https://github.com/neosubhamoy/neodlp-extension/blob/main/LICENSE). Anyone can view, modify, use (personal and commercial) or distribute it's sources without any attribution and extra permissions.
|
NeoDLP Extension is Licensed under the [MIT license](https://github.com/neosubhamoy/neodlp-extension/blob/main/LICENSE). Anyone can view, modify, use (personal and commercial) or distribute it's sources without any attribution and extra permissions.
|
||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
64
components/ui/tabs.tsx
Normal file
64
components/ui/tabs.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Tabs({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<TabsPrimitive.Root
|
||||||
|
data-slot="tabs"
|
||||||
|
className={cn("flex flex-col gap-2", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabsList({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TabsPrimitive.List>) {
|
||||||
|
return (
|
||||||
|
<TabsPrimitive.List
|
||||||
|
data-slot="tabs-list"
|
||||||
|
className={cn(
|
||||||
|
"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabsTrigger({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
|
||||||
|
return (
|
||||||
|
<TabsPrimitive.Trigger
|
||||||
|
data-slot="tabs-trigger"
|
||||||
|
className={cn(
|
||||||
|
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabsContent({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
|
||||||
|
return (
|
||||||
|
<TabsPrimitive.Content
|
||||||
|
data-slot="tabs-content"
|
||||||
|
className={cn("flex-1 outline-none", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
||||||
@@ -35,13 +35,13 @@ export default defineBackground(() => {
|
|||||||
|
|
||||||
// Listen for the keyboard commands
|
// Listen for the keyboard commands
|
||||||
browser.commands.onCommand.addListener(async (command) => {
|
browser.commands.onCommand.addListener(async (command) => {
|
||||||
if (command === "neodlp:quick-download") {
|
if (command === "neodlp:quick-search") {
|
||||||
try {
|
try {
|
||||||
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||||
const activeTab = tabs[0];
|
const activeTab = tabs[0];
|
||||||
|
|
||||||
if (activeTab && activeTab.url) {
|
if (activeTab && activeTab.url) {
|
||||||
console.log("Quick download triggered for URL:", activeTab.url);
|
console.log("Quick search triggered for URL:", activeTab.url);
|
||||||
|
|
||||||
const response = await sendMessageToNativeHost({
|
const response = await sendMessageToNativeHost({
|
||||||
url: activeTab.url,
|
url: activeTab.url,
|
||||||
@@ -49,12 +49,12 @@ export default defineBackground(() => {
|
|||||||
argument: ''
|
argument: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Quick download response:", response);
|
console.log("Quick search response:", response);
|
||||||
} else {
|
} else {
|
||||||
console.error("No active tab or URL found for quick download");
|
console.error("No active tab or URL found for quick search");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error in quick download:", error);
|
console.error("Error in quick search:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -63,50 +63,50 @@ export default defineBackground(() => {
|
|||||||
browser.contextMenus.removeAll().then(() => {
|
browser.contextMenus.removeAll().then(() => {
|
||||||
// Context menu for quick download
|
// Context menu for quick download
|
||||||
browser.contextMenus.create({
|
browser.contextMenus.create({
|
||||||
id: "quick-download:page",
|
id: "quick-search:page",
|
||||||
title: "Download with Neo Downloader Plus",
|
title: "Search with Neo Downloader Plus",
|
||||||
contexts: ["page"]
|
contexts: ["page"]
|
||||||
});
|
});
|
||||||
|
|
||||||
browser.contextMenus.create({
|
browser.contextMenus.create({
|
||||||
id: "quick-download:link",
|
id: "quick-search:link",
|
||||||
title: "Download Link with Neo Downloader Plus",
|
title: "Search with Neo Downloader Plus (Link)",
|
||||||
contexts: ["link"]
|
contexts: ["link"]
|
||||||
});
|
});
|
||||||
|
|
||||||
browser.contextMenus.create({
|
browser.contextMenus.create({
|
||||||
id: "quick-download:media",
|
id: "quick-search:media",
|
||||||
title: "Download Media with Neo Downloader Plus",
|
title: "Search with Neo Downloader Plus (Media Source)",
|
||||||
contexts: ["video", "audio"]
|
contexts: ["video", "audio"]
|
||||||
});
|
});
|
||||||
|
|
||||||
browser.contextMenus.create({
|
browser.contextMenus.create({
|
||||||
id: "quick-download:selection",
|
id: "quick-search:selection",
|
||||||
title: "Download Selection with Neo Downloader Plus",
|
title: "Search with Neo Downloader Plus (Selected Text)",
|
||||||
contexts: ["selection"]
|
contexts: ["selection"]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
browser.contextMenus.onClicked.addListener((info, tab) => {
|
browser.contextMenus.onClicked.addListener((info, tab) => {
|
||||||
let url = '';
|
let url = '';
|
||||||
if (info.menuItemId === "quick-download:page") {
|
if (info.menuItemId === "quick-search:page") {
|
||||||
if(!info.pageUrl) return;
|
if(!info.pageUrl) return;
|
||||||
url = info.pageUrl;
|
url = info.pageUrl;
|
||||||
} else if (info.menuItemId === "quick-download:link") {
|
} else if (info.menuItemId === "quick-search:link") {
|
||||||
if(!info.linkUrl) return;
|
if(!info.linkUrl) return;
|
||||||
url = info.linkUrl;
|
url = info.linkUrl;
|
||||||
} else if (info.menuItemId === "quick-download:media") {
|
} else if (info.menuItemId === "quick-search:media") {
|
||||||
if(!info.srcUrl) return;
|
if(!info.srcUrl) return;
|
||||||
url = info.srcUrl;
|
url = info.srcUrl;
|
||||||
} else if (info.menuItemId === "quick-download:selection") {
|
} else if (info.menuItemId === "quick-search:selection") {
|
||||||
if(!info.selectionText) return;
|
if(!info.selectionText) return;
|
||||||
url = info.selectionText;
|
url = info.selectionText;
|
||||||
}
|
}
|
||||||
if (!url) return;
|
if (!url) return;
|
||||||
sendMessageToNativeHost({url: url, command: 'download', argument: ''}).then(response => {
|
sendMessageToNativeHost({url: url, command: 'download', argument: ''}).then(response => {
|
||||||
console.log("Context menu download response:", response);
|
console.log("Context menu search response:", response);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error("Error in context menu download:", error);
|
console.error("Error in context menu search:", error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,77 +4,79 @@
|
|||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
--radius: 0.625rem;
|
||||||
--background: oklch(1 0 0);
|
--background: oklch(1 0 0);
|
||||||
--foreground: oklch(0.145 0 0);
|
--foreground: oklch(0.141 0.005 285.823);
|
||||||
--card: oklch(1 0 0);
|
--card: oklch(1 0 0);
|
||||||
--card-foreground: oklch(0.145 0 0);
|
--card-foreground: oklch(0.141 0.005 285.823);
|
||||||
--popover: oklch(1 0 0);
|
--popover: oklch(1 0 0);
|
||||||
--popover-foreground: oklch(0.145 0 0);
|
--popover-foreground: oklch(0.141 0.005 285.823);
|
||||||
--primary: oklch(0.205 0 0);
|
--primary: oklch(0.21 0.006 285.885);
|
||||||
--primary-foreground: oklch(0.985 0 0);
|
--primary-foreground: oklch(0.985 0 0);
|
||||||
--secondary: oklch(0.97 0 0);
|
--secondary: oklch(0.967 0.001 286.375);
|
||||||
--secondary-foreground: oklch(0.205 0 0);
|
--secondary-foreground: oklch(0.21 0.006 285.885);
|
||||||
--muted: oklch(0.97 0 0);
|
--muted: oklch(0.967 0.001 286.375);
|
||||||
--muted-foreground: oklch(0.556 0 0);
|
--muted-foreground: oklch(0.552 0.016 285.938);
|
||||||
--accent: oklch(0.97 0 0);
|
--accent: oklch(0.967 0.001 286.375);
|
||||||
--accent-foreground: oklch(0.205 0 0);
|
--accent-foreground: oklch(0.21 0.006 285.885);
|
||||||
--destructive: oklch(0.577 0.245 27.325);
|
--destructive: oklch(0.577 0.245 27.325);
|
||||||
--destructive-foreground: oklch(0.577 0.245 27.325);
|
--border: oklch(0.92 0.004 286.32);
|
||||||
--border: oklch(0.922 0 0);
|
--input: oklch(0.92 0.004 286.32);
|
||||||
--input: oklch(0.922 0 0);
|
--ring: oklch(0.705 0.015 286.067);
|
||||||
--ring: oklch(0.708 0 0);
|
|
||||||
--chart-1: oklch(0.646 0.222 41.116);
|
--chart-1: oklch(0.646 0.222 41.116);
|
||||||
--chart-2: oklch(0.6 0.118 184.704);
|
--chart-2: oklch(0.6 0.118 184.704);
|
||||||
--chart-3: oklch(0.398 0.07 227.392);
|
--chart-3: oklch(0.398 0.07 227.392);
|
||||||
--chart-4: oklch(0.828 0.189 84.429);
|
--chart-4: oklch(0.828 0.189 84.429);
|
||||||
--chart-5: oklch(0.769 0.188 70.08);
|
--chart-5: oklch(0.769 0.188 70.08);
|
||||||
--radius: 0.625rem;
|
|
||||||
--sidebar: oklch(0.985 0 0);
|
--sidebar: oklch(0.985 0 0);
|
||||||
--sidebar-foreground: oklch(0.145 0 0);
|
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
||||||
--sidebar-primary: oklch(0.205 0 0);
|
--sidebar-primary: oklch(0.21 0.006 285.885);
|
||||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-accent: oklch(0.97 0 0);
|
--sidebar-accent: oklch(0.967 0.001 286.375);
|
||||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
||||||
--sidebar-border: oklch(0.922 0 0);
|
--sidebar-border: oklch(0.92 0.004 286.32);
|
||||||
--sidebar-ring: oklch(0.708 0 0);
|
--sidebar-ring: oklch(0.705 0.015 286.067);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: oklch(0.145 0 0);
|
--background: oklch(0.141 0.005 285.823);
|
||||||
--foreground: oklch(0.985 0 0);
|
--foreground: oklch(0.985 0 0);
|
||||||
--card: oklch(0.145 0 0);
|
--card: oklch(0.21 0.006 285.885);
|
||||||
--card-foreground: oklch(0.985 0 0);
|
--card-foreground: oklch(0.985 0 0);
|
||||||
--popover: oklch(0.145 0 0);
|
--popover: oklch(0.21 0.006 285.885);
|
||||||
--popover-foreground: oklch(0.985 0 0);
|
--popover-foreground: oklch(0.985 0 0);
|
||||||
--primary: oklch(0.985 0 0);
|
--primary: oklch(0.92 0.004 286.32);
|
||||||
--primary-foreground: oklch(0.205 0 0);
|
--primary-foreground: oklch(0.21 0.006 285.885);
|
||||||
--secondary: oklch(0.269 0 0);
|
--secondary: oklch(0.274 0.006 286.033);
|
||||||
--secondary-foreground: oklch(0.985 0 0);
|
--secondary-foreground: oklch(0.985 0 0);
|
||||||
--muted: oklch(0.269 0 0);
|
--muted: oklch(0.274 0.006 286.033);
|
||||||
--muted-foreground: oklch(0.708 0 0);
|
--muted-foreground: oklch(0.705 0.015 286.067);
|
||||||
--accent: oklch(0.269 0 0);
|
--accent: oklch(0.274 0.006 286.033);
|
||||||
--accent-foreground: oklch(0.985 0 0);
|
--accent-foreground: oklch(0.985 0 0);
|
||||||
--destructive: oklch(0.396 0.141 25.723);
|
--destructive: oklch(0.704 0.191 22.216);
|
||||||
--destructive-foreground: oklch(0.637 0.237 25.331);
|
--border: oklch(1 0 0 / 10%);
|
||||||
--border: oklch(0.269 0 0);
|
--input: oklch(1 0 0 / 15%);
|
||||||
--input: oklch(0.269 0 0);
|
--ring: oklch(0.552 0.016 285.938);
|
||||||
--ring: oklch(0.439 0 0);
|
|
||||||
--chart-1: oklch(0.488 0.243 264.376);
|
--chart-1: oklch(0.488 0.243 264.376);
|
||||||
--chart-2: oklch(0.696 0.17 162.48);
|
--chart-2: oklch(0.696 0.17 162.48);
|
||||||
--chart-3: oklch(0.769 0.188 70.08);
|
--chart-3: oklch(0.769 0.188 70.08);
|
||||||
--chart-4: oklch(0.627 0.265 303.9);
|
--chart-4: oklch(0.627 0.265 303.9);
|
||||||
--chart-5: oklch(0.645 0.246 16.439);
|
--chart-5: oklch(0.645 0.246 16.439);
|
||||||
--sidebar: oklch(0.205 0 0);
|
--sidebar: oklch(0.21 0.006 285.885);
|
||||||
--sidebar-foreground: oklch(0.985 0 0);
|
--sidebar-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-accent: oklch(0.269 0 0);
|
--sidebar-accent: oklch(0.274 0.006 286.033);
|
||||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-border: oklch(0.269 0 0);
|
--sidebar-border: oklch(1 0 0 / 10%);
|
||||||
--sidebar-ring: oklch(0.439 0 0);
|
--sidebar-ring: oklch(0.552 0.016 285.938);
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
|
--radius-lg: var(--radius);
|
||||||
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
--color-background: var(--background);
|
--color-background: var(--background);
|
||||||
--color-foreground: var(--foreground);
|
--color-foreground: var(--foreground);
|
||||||
--color-card: var(--card);
|
--color-card: var(--card);
|
||||||
@@ -90,7 +92,6 @@
|
|||||||
--color-accent: var(--accent);
|
--color-accent: var(--accent);
|
||||||
--color-accent-foreground: var(--accent-foreground);
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
--color-destructive: var(--destructive);
|
--color-destructive: var(--destructive);
|
||||||
--color-destructive-foreground: var(--destructive-foreground);
|
|
||||||
--color-border: var(--border);
|
--color-border: var(--border);
|
||||||
--color-input: var(--input);
|
--color-input: var(--input);
|
||||||
--color-ring: var(--ring);
|
--color-ring: var(--ring);
|
||||||
@@ -99,10 +100,6 @@
|
|||||||
--color-chart-3: var(--chart-3);
|
--color-chart-3: var(--chart-3);
|
||||||
--color-chart-4: var(--chart-4);
|
--color-chart-4: var(--chart-4);
|
||||||
--color-chart-5: var(--chart-5);
|
--color-chart-5: var(--chart-5);
|
||||||
--radius-sm: calc(var(--radius) - 4px);
|
|
||||||
--radius-md: calc(var(--radius) - 2px);
|
|
||||||
--radius-lg: var(--radius);
|
|
||||||
--radius-xl: calc(var(--radius) + 4px);
|
|
||||||
--color-sidebar: var(--sidebar);
|
--color-sidebar: var(--sidebar);
|
||||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
--color-sidebar-primary: var(--sidebar-primary);
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
|
|||||||
2445
package-lock.json
generated
2445
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
47
package.json
47
package.json
@@ -2,7 +2,7 @@
|
|||||||
"name": "neodlp-extension",
|
"name": "neodlp-extension",
|
||||||
"description": "NeoDLP Browser Integration",
|
"description": "NeoDLP Browser Integration",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "wxt",
|
"dev": "wxt",
|
||||||
@@ -12,33 +12,34 @@
|
|||||||
"zip": "wxt zip",
|
"zip": "wxt zip",
|
||||||
"zip:firefox": "wxt zip -b firefox",
|
"zip:firefox": "wxt zip -b firefox",
|
||||||
"compile": "tsc --noEmit",
|
"compile": "tsc --noEmit",
|
||||||
"postinstall": "wxt prepare"
|
"postinstall": "wxt prepare",
|
||||||
|
"submit": "wxt submit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^5.0.1",
|
"@hookform/resolvers": "^5.2.2",
|
||||||
"@radix-ui/react-label": "^2.1.4",
|
"@radix-ui/react-label": "^2.1.8",
|
||||||
"@radix-ui/react-slot": "^1.2.0",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
"@radix-ui/react-switch": "^1.2.2",
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
"@radix-ui/react-tabs": "^1.1.8",
|
"@radix-ui/react-tabs": "^1.1.13",
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@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.501.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.56.0",
|
"react-hook-form": "^7.66.0",
|
||||||
"react-router-dom": "^7.5.1",
|
"react-router-dom": "^7.9.5",
|
||||||
"tailwind-merge": "^3.2.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
"tailwindcss": "^4.1.4",
|
"tailwindcss": "^4.1.17",
|
||||||
"tw-animate-css": "^1.2.7",
|
"tw-animate-css": "^1.4.0",
|
||||||
"zod": "^3.24.3"
|
"zod": "^4.1.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^24.10.0",
|
||||||
"@types/react": "^19.1.0",
|
"@types/react": "^19.2.2",
|
||||||
"@types/react-dom": "^19.1.2",
|
"@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.0"
|
"wxt": "^0.20.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { AlertCircle, Download, Loader2, LucideIcon, Monitor, Moon, Sun } from "lucide-react";
|
import { AlertCircle, Loader2, LucideIcon, Monitor, Moon, Search, Sun } from "lucide-react";
|
||||||
import { type Browser } from 'wxt/browser';
|
import { type Browser } from 'wxt/browser';
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -11,16 +11,22 @@ 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 searchFormSchema = z.object({
|
||||||
url: z.string().min(1, { message: "URL is required" }).url({message: "Invalid URL format." }),
|
url: z.url({
|
||||||
|
error: (issue) => issue.input === undefined || issue.input === null || issue.input === ""
|
||||||
|
? "URL is required"
|
||||||
|
: "Invalid URL format"
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
const { setTheme } = useTheme();
|
const { setTheme } = useTheme();
|
||||||
const [isDownloading, setIsDownloading] = useState(false);
|
const [isSearching, setIsSearching] = useState(false);
|
||||||
const [isLoadingSettings, setIsLoadingSettings] = useState(true);
|
const [isLoadingSettings, setIsLoadingSettings] = useState(true);
|
||||||
const [isUpdatingSettings, setIsUpdatingSettings] = useState(false);
|
const [isUpdatingSettings, setIsUpdatingSettings] = useState(false);
|
||||||
const [showNotRunningAlert, setShowNotRunningAlert] = useState(false);
|
const [showNotRunningAlert, setShowNotRunningAlert] = useState(false);
|
||||||
@@ -28,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' },
|
||||||
@@ -35,17 +42,17 @@ export default function HomePage() {
|
|||||||
{ value: 'system', icon: Monitor, label: 'System' },
|
{ value: 'system', icon: Monitor, label: 'System' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const downloadForm = useForm<z.infer<typeof downloadFormSchema>>({
|
const searchForm = useForm<z.infer<typeof searchFormSchema>>({
|
||||||
resolver: zodResolver(downloadFormSchema),
|
resolver: zodResolver(searchFormSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
url: '',
|
url: '',
|
||||||
},
|
},
|
||||||
mode: "onChange",
|
mode: "onChange",
|
||||||
});
|
});
|
||||||
const watchedUrl = downloadForm.watch("url");
|
const watchedUrl = searchForm.watch("url");
|
||||||
|
|
||||||
const handleDownload = async (url?: string) => {
|
const handleSearch = async (url?: string) => {
|
||||||
setIsDownloading(true);
|
setIsSearching(true);
|
||||||
setShowNotRunningAlert(false); // Reset alert status at the beginning
|
setShowNotRunningAlert(false); // Reset alert status at the beginning
|
||||||
|
|
||||||
// Create a timeout reference with undefined type
|
// Create a timeout reference with undefined type
|
||||||
@@ -78,7 +85,7 @@ export default function HomePage() {
|
|||||||
console.log('Response from background script:', response);
|
console.log('Response from background script:', response);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Download failed", error);
|
console.error("Search failed", error);
|
||||||
|
|
||||||
// Check if this was a timeout error
|
// Check if this was a timeout error
|
||||||
if (error instanceof Error && error.message === 'TIMEOUT') {
|
if (error instanceof Error && error.message === 'TIMEOUT') {
|
||||||
@@ -88,12 +95,12 @@ export default function HomePage() {
|
|||||||
// Clear the timeout if it was some other error
|
// Clear the timeout if it was some other error
|
||||||
if (timeoutId) clearTimeout(timeoutId);
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
} finally {
|
} finally {
|
||||||
setIsDownloading(false);
|
setIsSearching(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDownloadSubmit = async (values: z.infer<typeof downloadFormSchema>) => {
|
const handleSearchSubmit = async (values: z.infer<typeof searchFormSchema>) => {
|
||||||
await handleDownload(values.url);
|
await handleSearch(values.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveSettings = async <K extends keyof Settings>(key: K, value: Settings[K]) => {
|
const saveSettings = async <K extends keyof Settings>(key: K, value: Settings[K]) => {
|
||||||
@@ -126,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 () => {
|
||||||
@@ -157,8 +171,8 @@ export default function HomePage() {
|
|||||||
});
|
});
|
||||||
const activeTab = tabs[0];
|
const activeTab = tabs[0];
|
||||||
if (activeTab && activeTab.url) {
|
if (activeTab && activeTab.url) {
|
||||||
downloadForm.setValue("url", activeTab.url);
|
searchForm.setValue("url", activeTab.url);
|
||||||
await downloadForm.trigger("url");
|
await searchForm.trigger("url");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isLoadingSettings && settings.autofill_url) {
|
if (!isLoadingSettings && settings.autofill_url) {
|
||||||
@@ -169,12 +183,12 @@ 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) {
|
||||||
downloadForm.setValue("url", tab.url);
|
searchForm.setValue("url", tab.url);
|
||||||
await downloadForm.trigger("url");
|
await searchForm.trigger("url");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -222,12 +236,12 @@ export default function HomePage() {
|
|||||||
/>
|
/>
|
||||||
<Label htmlFor="autofill-url">AutoFill Page URL</Label>
|
<Label htmlFor="autofill-url">AutoFill Page URL</Label>
|
||||||
</div>
|
</div>
|
||||||
<Form {...downloadForm}>
|
<Form {...searchForm}>
|
||||||
<form onSubmit={downloadForm.handleSubmit(handleDownloadSubmit)} className="flex flex-col gap-4 w-full" autoComplete="off">
|
<form onSubmit={searchForm.handleSubmit(handleSearchSubmit)} className="flex flex-col gap-4 w-full" autoComplete="off">
|
||||||
<FormField
|
<FormField
|
||||||
control={downloadForm.control}
|
control={searchForm.control}
|
||||||
name="url"
|
name="url"
|
||||||
disabled={isDownloading || isLoadingSettings || isUpdatingSettings}
|
disabled={isSearching || isLoadingSettings || isUpdatingSettings}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="w-full">
|
<FormItem className="w-full">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
@@ -251,21 +265,44 @@ export default function HomePage() {
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Button className="w-full cursor-pointer" type="submit" disabled={isDownloading || isLoadingSettings || isUpdatingSettings || !watchedUrl || downloadForm.getFieldState("url").invalid}>
|
<Button className="w-full cursor-pointer" type="submit" disabled={isSearching || isLoadingSettings || isUpdatingSettings || !watchedUrl || searchForm.getFieldState("url").invalid}>
|
||||||
{isDownloading ? (
|
{isSearching ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 animate-spin" />
|
<Loader2 className="w-4 h-4 animate-spin" />
|
||||||
Starting
|
Initiating
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Download className="w-4 h-4" />
|
<Search className="w-4 h-4" />
|
||||||
Download
|
Search
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</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-search-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-search")?.shortcut ? (
|
||||||
|
<KbdGroup>
|
||||||
|
{shortcuts.find(cmd => cmd.name === "neodlp:quick-search")?.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ export default defineConfig({
|
|||||||
name: "Neo Downloader Plus",
|
name: "Neo Downloader Plus",
|
||||||
description: "Neo Downloader Plus Browser Integration",
|
description: "Neo Downloader Plus Browser Integration",
|
||||||
homepage_url: "https://neodlp.neosubhamoy.com",
|
homepage_url: "https://neodlp.neosubhamoy.com",
|
||||||
version: "0.1.0",
|
version: "0.1.1",
|
||||||
permissions: ["tabs", "storage", "contextMenus", "nativeMessaging"],
|
permissions: ["tabs", "storage", "contextMenus", "nativeMessaging"],
|
||||||
}
|
}
|
||||||
if (browser === 'chrome') {
|
if (browser === 'chrome') {
|
||||||
@@ -33,8 +33,8 @@ export default defineConfig({
|
|||||||
default: "Alt+Shift+N",
|
default: "Alt+Shift+N",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"neodlp:quick-download": {
|
"neodlp:quick-search": {
|
||||||
description: "Quick Download",
|
description: "Quick Search",
|
||||||
suggested_key: {
|
suggested_key: {
|
||||||
default: "Alt+Shift+Q"
|
default: "Alt+Shift+Q"
|
||||||
}
|
}
|
||||||
@@ -55,8 +55,8 @@ export default defineConfig({
|
|||||||
default: "Alt+Shift+N",
|
default: "Alt+Shift+N",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"neodlp:quick-download": {
|
"neodlp:quick-search": {
|
||||||
description: "Quick Download",
|
description: "Quick Search",
|
||||||
suggested_key: {
|
suggested_key: {
|
||||||
default: "Alt+Shift+Q"
|
default: "Alt+Shift+Q"
|
||||||
}
|
}
|
||||||
@@ -72,8 +72,8 @@ export default defineConfig({
|
|||||||
default: "Alt+Shift+N",
|
default: "Alt+Shift+N",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"neodlp:quick-download": {
|
"neodlp:quick-search": {
|
||||||
description: "Quick Download",
|
description: "Quick Search",
|
||||||
suggested_key: {
|
suggested_key: {
|
||||||
default: "Alt+Shift+Q"
|
default: "Alt+Shift+Q"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user