commit bd9483374f9b2577859a8db8df4c95bb54b574d1 Author: Subhamoy Biswas Date: Thu May 1 21:39:12 2025 +0530 (chore): initial MVP release v0.1.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9f5878e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,90 @@ +on: + push: + tags: + - 'v*.*.*' + +name: 🚀 Release on GitHub +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: 🚚 Checkout repository + uses: actions/checkout@v4 + + - name: 📦 Setup pnpm + uses: pnpm/action-setup@v4 + + - name: 📦 Setup node + uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + cache: 'pnpm' + + - name: 🛠️ Install dependencies + run: pnpm install + + - name: 🤐 Zip extensions + run: | + pnpm zip + pnpm zip:firefox + + - name: ✨ Extract version number + id: extract_version + run: | + VERSION_NUM=$(echo "${{ github.ref_name }}" | sed -E 's/^v([0-9]+\.[0-9]+\.[0-9]+)(-.*)?$/\1/') + echo "version=$VERSION_NUM" >> $GITHUB_ENV + + - name: 📄 Read and process changelog + id: changelog + shell: bash + run: | + if [ -f CHANGELOG.md ]; then + # Read and replace placeholders + CONTENT=$(cat CHANGELOG.md) + CONTENT=${CONTENT///${{ github.ref_name }}} + CONTENT=${CONTENT///${{ env.version }}} + + echo "content<> $GITHUB_OUTPUT + echo "$CONTENT" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + # Also save to a processed file for later use + echo "$CONTENT" > CHANGELOG_PROCESSED.md + else + echo "content=No changelog found" >> $GITHUB_OUTPUT + echo "No changelog found" > CHANGELOG_PROCESSED.md + fi + + - name: 📝 Create latest.json + id: create_latest_json + shell: bash + run: | + # Create latest.json + cat > latest.json << EOF + { + "version": "${{env.version}}", + "notes": $(cat CHANGELOG_PROCESSED.md | jq -s -R .), + "browsers": { + "chrome": { + "url": "https://github.com/${{github.repository}}/releases/download/${{github.ref_name}}/${{github.event.repository.name}}-${{env.version}}-chrome.zip" + }, + "firefox": { + "url": "https://github.com/${{github.repository}}/releases/download/${{github.ref_name}}/${{github.event.repository.name}}-${{env.version}}-firefox.zip" + } + } + } + EOF + + - name: 🚀 Upload to github releases + uses: softprops/action-gh-release@v2 + with: + name: ${{github.event.repository.name}}-${{github.ref_name}} + body_path: CHANGELOG_PROCESSED.md + files: | + .output/${{github.event.repository.name}}-${{env.version}}-chrome.zip + .output/${{github.event.repository.name}}-${{env.version}}-firefox.zip + .output/${{github.event.repository.name}}-${{env.version}}-sources.zip + latest.json + draft: false + prerelease: false + make_latest: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a256953 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.output +stats.html +stats-*.json +.wxt +web-ext.config.ts + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c406097 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +### ✨ Changelog + +- initial MVP release v0.1.0 + +### ⬇️ Download Section + +| Type\Browser | Chrome / Chromium based | Firefox / Firefox based | +| :---- | :---- | :---- | +| Store Listed (Recommended - Auto updates) | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | +| Unpackable ZIP (Manual updates) | [Download](https://github.com/neosubhamoy/neodlp-extension/releases/download//neodlp-extension--chrome.zip) | [Download](https://github.com/neosubhamoy/neodlp-extension/releases/download//neodlp-extension--firefox.zip) | \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c20235d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Subhamoy Biswas + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5735699 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# NeoDLP Extension + +NeoDLP (Neo Downloader Plus) Browser Integration + +[![status](https://img.shields.io/badge/status-active-brightgreen.svg?style=flat)](https://github.com/neosubhamoy/neodlp-extension) +[![github tag](https://img.shields.io/github/v/tag/neosubhamoy/neodlp-extension?color=yellow)](https://github.com/neosubhamoy/neodlp-extension) +[![PRs](https://img.shields.io/badge/PRs-welcome-blue.svg?style=flat)](https://github.com/neosubhamoy/neodlp-extension) + +> **🥰 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 + +- [NeoDLP](https://github.com/neosubhamoy/neodlp) (Installed and Running) + +### ⬇️ Download and Installation + +#### Manual Installation (in Google Chrome / Chromium based browsers) + +1. Download the `neodlp-extension--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.). +3. Open Google Chrome and navigate to `chrome://extensions/`. +4. Enable "Developer mode" in the top right corner. +5. Click on "Load unpacked" and select the newly extracted folder. Done! That's it...!! + +#### Manual Installation (in Mozilla Firefox) + +1. Download the `neodlp-extension--firefox.zip` file from the [latest release](https://github.com/neosubhamoy/neodlp-extension/releases). +2. Open Firefox and navigate to `about:addons`. +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...!! + +### ⚡ Technologies Used + +![WXT](https://img.shields.io/badge/WXT-67D55E.svg?style=for-the-badge&logo=WXT&logoColor=white) +![React](https://img.shields.io/badge/React-61DAFB.svg?style=for-the-badge&logo=React&logoColor=black) +![TypeScript](https://img.shields.io/badge/TypeScript-3178C6.svg?style=for-the-badge&logo=TypeScript&logoColor=white) +![ShadCnUi](https://img.shields.io/badge/shadcn/ui-000000.svg?style=for-the-badge&logo=shadcn/ui&logoColor=white) + +### 🛠️ 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: + +* Make sure to install Node.js, Git before proceeding. +1. Fork this repo in your github account. +2. Git clone the forked repo in your local machine. +3. Install Node.js dependencies: `npm install` +4. Run development / build / zipping process +```code +npm run dev # for development (chrome) +npm run dev:firefox # for development (firefox) +npm run build # for production build (chrome) +npm run build:firefox # for production build (firefox) +npm run zip # for production zip creation (chrome) +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) + +**⭕ 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...!!** + +### 📝 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. \ No newline at end of file diff --git a/components.json b/components.json new file mode 100644 index 0000000..08ec315 --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "entrypoints/popup/style.css", + "baseColor": "zinc", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/components/footer.tsx b/components/footer.tsx new file mode 100644 index 0000000..49517b4 --- /dev/null +++ b/components/footer.tsx @@ -0,0 +1,13 @@ + + +export default function Footer() { + const manifest = browser.runtime.getManifest(); + + return ( + + ) +} \ No newline at end of file diff --git a/components/header.tsx b/components/header.tsx new file mode 100644 index 0000000..beba7a8 --- /dev/null +++ b/components/header.tsx @@ -0,0 +1,18 @@ +import { Card } from "@/components/ui/card"; +import { NeoDlpLogo } from "@/components/icons/neodlp"; + +export default function Header() { + const manifest = browser.runtime.getManifest(); + + return ( + +
+ +
+

{manifest.name}

+

Extension - v{manifest.version}

+
+
+
+ ) +} \ No newline at end of file diff --git a/components/icons/neodlp.tsx b/components/icons/neodlp.tsx new file mode 100644 index 0000000..46f5b43 --- /dev/null +++ b/components/icons/neodlp.tsx @@ -0,0 +1,15 @@ +export function NeoDlpLogo({ className }: { className?: string }) { + return ( + + + + + + + + + + + + ) +} \ No newline at end of file diff --git a/components/theme-provider.tsx b/components/theme-provider.tsx new file mode 100644 index 0000000..ee5f2db --- /dev/null +++ b/components/theme-provider.tsx @@ -0,0 +1,73 @@ +import { createContext, useContext, useEffect, useState } from "react" + +type Theme = "dark" | "light" | "system" + +type ThemeProviderProps = { + children: React.ReactNode + defaultTheme?: Theme + storageKey?: string +} + +type ThemeProviderState = { + theme: Theme + setTheme: (theme: Theme) => void +} + +const initialState: ThemeProviderState = { + theme: "system", + setTheme: () => null, +} + +const ThemeProviderContext = createContext(initialState) + +export function ThemeProvider({ + children, + defaultTheme = "system", + storageKey = "vite-ui-theme", + ...props +}: ThemeProviderProps) { + const [theme, setTheme] = useState( + () => (localStorage.getItem(storageKey) as Theme) || defaultTheme + ) + + useEffect(() => { + const root = window.document.documentElement + + root.classList.remove("light", "dark") + + if (theme === "system") { + const systemTheme = window.matchMedia("(prefers-color-scheme: dark)") + .matches + ? "dark" + : "light" + + root.classList.add(systemTheme) + return + } + + root.classList.add(theme) + }, [theme]) + + const value = { + theme, + setTheme: (theme: Theme) => { + localStorage.setItem(storageKey, theme) + setTheme(theme) + }, + } + + return ( + + {children} + + ) +} + +export const useTheme = () => { + const context = useContext(ThemeProviderContext) + + if (context === undefined) + throw new Error("useTheme must be used within a ThemeProvider") + + return context +} \ No newline at end of file diff --git a/components/ui/alert.tsx b/components/ui/alert.tsx new file mode 100644 index 0000000..1421354 --- /dev/null +++ b/components/ui/alert.tsx @@ -0,0 +1,66 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", + { + variants: { + variant: { + default: "bg-card text-card-foreground", + destructive: + "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDescription({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { Alert, AlertTitle, AlertDescription } diff --git a/components/ui/button.tsx b/components/ui/button.tsx new file mode 100644 index 0000000..a2df8dc --- /dev/null +++ b/components/ui/button.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", + 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", + 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", + secondary: + "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + 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", + icon: "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot : "button" + + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/components/ui/card.tsx b/components/ui/card.tsx new file mode 100644 index 0000000..d05bbc6 --- /dev/null +++ b/components/ui/card.tsx @@ -0,0 +1,92 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Card({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardAction({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardAction, + CardDescription, + CardContent, +} diff --git a/components/ui/form.tsx b/components/ui/form.tsx new file mode 100644 index 0000000..7d7474c --- /dev/null +++ b/components/ui/form.tsx @@ -0,0 +1,165 @@ +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { Slot } from "@radix-ui/react-slot" +import { + Controller, + FormProvider, + useFormContext, + useFormState, + type ControllerProps, + type FieldPath, + type FieldValues, +} from "react-hook-form" + +import { cn } from "@/lib/utils" +import { Label } from "@/components/ui/label" + +const Form = FormProvider + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName +} + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue +) + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ) +} + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext) + const itemContext = React.useContext(FormItemContext) + const { getFieldState } = useFormContext() + const formState = useFormState({ name: fieldContext.name }) + const fieldState = getFieldState(fieldContext.name, formState) + + if (!fieldContext) { + throw new Error("useFormField should be used within ") + } + + const { id } = itemContext + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + } +} + +type FormItemContextValue = { + id: string +} + +const FormItemContext = React.createContext( + {} as FormItemContextValue +) + +function FormItem({ className, ...props }: React.ComponentProps<"div">) { + const id = React.useId() + + return ( + +
+ + ) +} + +function FormLabel({ + className, + ...props +}: React.ComponentProps) { + const { error, formItemId } = useFormField() + + return ( +