don't know
This commit is contained in:
parent
10c39c6143
commit
6773009bd0
|
|
@ -1,383 +1,566 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Projects - Brand Design</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://unpkg.com/lucide@latest"></script>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap');
|
||||
body { font-family: 'Inter', sans-serif; }
|
||||
|
||||
/* Brand Colors */
|
||||
:root {
|
||||
--brand-blue: #00A3FF;
|
||||
}
|
||||
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap");
|
||||
body {
|
||||
font-family: "Inter", sans-serif;
|
||||
}
|
||||
|
||||
.project-card:hover .card-actions { opacity: 1; }
|
||||
.checkbox-wrapper:checked + div { border-color: var(--brand-blue); background-color: #eff6ff; }
|
||||
.controls-row.has-selection .controls-select { opacity: 1; }
|
||||
.project-select-wrapper { opacity: 0; }
|
||||
.project-card:hover .project-select-wrapper { opacity: 1; }
|
||||
.project-card:has(.project-select:checked) .project-select-wrapper { opacity: 1; }
|
||||
.multi-select .project-menu { opacity: 0 !important; pointer-events: none; }
|
||||
|
||||
/* Custom Checkbox - Softer and Rounder */
|
||||
.custom-checkbox {
|
||||
appearance: none;
|
||||
background-color: #fff;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px; /* Rounder checkbox */
|
||||
display: inline-grid;
|
||||
place-content: center;
|
||||
}
|
||||
.custom-checkbox:checked {
|
||||
background-color: var(--brand-blue);
|
||||
border-color: var(--brand-blue);
|
||||
}
|
||||
.custom-checkbox:checked::after {
|
||||
content: "";
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: #fff;
|
||||
clip-path: polygon(14% 44%, 0 58%, 40% 100%, 100% 24%, 86% 10%, 40% 70%);
|
||||
}
|
||||
/* Brand Colors */
|
||||
:root {
|
||||
--brand-blue: #00a3ff;
|
||||
}
|
||||
|
||||
.project-card:hover .card-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
.checkbox-wrapper:checked + div {
|
||||
border-color: var(--brand-blue);
|
||||
background-color: #eff6ff;
|
||||
}
|
||||
.controls-row.has-selection .controls-select {
|
||||
opacity: 1;
|
||||
}
|
||||
.project-select-wrapper {
|
||||
opacity: 0;
|
||||
}
|
||||
.project-card:hover .project-select-wrapper {
|
||||
opacity: 1;
|
||||
}
|
||||
.project-card:has(.project-select:checked) .project-select-wrapper {
|
||||
opacity: 1;
|
||||
}
|
||||
.multi-select .project-menu {
|
||||
opacity: 0 !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Custom Checkbox - Softer and Rounder */
|
||||
.custom-checkbox {
|
||||
appearance: none;
|
||||
background-color: #fff;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px; /* Rounder checkbox */
|
||||
display: inline-grid;
|
||||
place-content: center;
|
||||
}
|
||||
.custom-checkbox:checked {
|
||||
background-color: var(--brand-blue);
|
||||
border-color: var(--brand-blue);
|
||||
}
|
||||
.custom-checkbox:checked::after {
|
||||
content: "";
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: #fff;
|
||||
clip-path: polygon(
|
||||
14% 44%,
|
||||
0 58%,
|
||||
40% 100%,
|
||||
100% 24%,
|
||||
86% 10%,
|
||||
40% 70%
|
||||
);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-[#ffffff] text-slate-900 h-screen flex overflow-hidden">
|
||||
|
||||
</head>
|
||||
<body class="bg-[#ffffff] text-slate-900 h-screen flex overflow-hidden">
|
||||
<!-- Sidebar -->
|
||||
<aside class="w-64 bg-white border-r border-slate-100 flex flex-col flex-shrink-0">
|
||||
<div class="p-6 pl-8 flex items-center gap-3">
|
||||
<img src="../apps/web/public/logos/opencut/1k/logo-black.png" alt="OpenCut Logo" class="size-6">
|
||||
<span class="font-semibold text-lg tracking-tight text-slate-900">OpenCut</span>
|
||||
<aside
|
||||
class="w-64 bg-white border-r border-slate-100 flex flex-col flex-shrink-0"
|
||||
>
|
||||
<div class="p-6 pl-8 flex items-center gap-3">
|
||||
<img
|
||||
src="../apps/web/public/logos/opencut/1k/logo-black.png"
|
||||
alt="OpenCut Logo"
|
||||
class="size-6"
|
||||
/>
|
||||
<span class="font-semibold text-lg tracking-tight text-slate-900"
|
||||
>OpenCut</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<nav class="flex-1 px-4 space-y-2 mt-2">
|
||||
<a
|
||||
href="#"
|
||||
class="flex items-center gap-3 px-4 py-3 text-sm font-medium bg-blue-50/50 text-[#00A3FF] rounded-2xl"
|
||||
>
|
||||
<i data-lucide="layout-grid" class="w-5 h-5"></i>
|
||||
Projects
|
||||
</a>
|
||||
<div class="pt-6 pb-2 px-4">
|
||||
<p class="text-xs font-bold text-slate-400 uppercase tracking-wider">
|
||||
Folders
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<nav class="flex-1 px-4 space-y-2 mt-2">
|
||||
<a href="#" class="flex items-center gap-3 px-4 py-3 text-sm font-medium bg-blue-50/50 text-[#00A3FF] rounded-2xl">
|
||||
<i data-lucide="layout-grid" class="w-5 h-5"></i>
|
||||
Projects
|
||||
</a>
|
||||
<div class="pt-6 pb-2 px-4">
|
||||
<p class="text-xs font-bold text-slate-400 uppercase tracking-wider">Folders</p>
|
||||
</div>
|
||||
<a href="#" class="flex items-center gap-3 px-4 py-3 text-sm font-medium text-slate-500 hover:bg-slate-50 hover:text-slate-900 rounded-2xl">
|
||||
<i data-lucide="folder" class="w-5 h-5 text-slate-400"></i>
|
||||
Marketing
|
||||
</a>
|
||||
<a href="#" class="flex items-center gap-3 px-4 py-3 text-sm font-medium text-slate-500 hover:bg-slate-50 hover:text-slate-900 rounded-2xl">
|
||||
<i data-lucide="folder" class="w-5 h-5 text-slate-400"></i>
|
||||
Social media
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<a
|
||||
href="#"
|
||||
class="flex items-center gap-3 px-4 py-3 text-sm font-medium text-slate-500 hover:bg-slate-50 hover:text-slate-900 rounded-2xl"
|
||||
>
|
||||
<i data-lucide="folder" class="w-5 h-5 text-slate-400"></i>
|
||||
Marketing
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
class="flex items-center gap-3 px-4 py-3 text-sm font-medium text-slate-500 hover:bg-slate-50 hover:text-slate-900 rounded-2xl"
|
||||
>
|
||||
<i data-lucide="folder" class="w-5 h-5 text-slate-400"></i>
|
||||
Social media
|
||||
</a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="flex-1 flex flex-col min-w-0">
|
||||
<!-- Header -->
|
||||
<header class="h-20 flex items-center justify-between px-8 flex-shrink-0">
|
||||
<div class="flex items-center gap-4">
|
||||
<h1 class="text-xl font-bold text-slate-900">All projects</h1>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="relative group">
|
||||
<i data-lucide="search" class="absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400"></i>
|
||||
<input id="search-input" type="text" placeholder="Search..." class="w-64 bg-white border border-slate-200 text-slate-900 text-sm rounded-full pl-10 pr-4 py-2.5 focus:outline-none focus:border-[#00A3FF] focus:ring-4 focus:ring-blue-500/10 placeholder:text-slate-400 shadow-sm">
|
||||
</div>
|
||||
|
||||
<div class="flex bg-white rounded-full border border-slate-200 p-1 shadow-sm">
|
||||
<button class="p-2 rounded-full bg-slate-100 text-slate-900 shadow-sm">
|
||||
<i data-lucide="layout-grid" class="w-4 h-4"></i>
|
||||
</button>
|
||||
<button class="p-2 rounded-full text-slate-400 hover:text-slate-600">
|
||||
<i data-lucide="list" class="w-4 h-4"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button class="bg-[#00A3FF] hover:bg-[#008BE0] text-white px-5 py-2.5 rounded-full text-sm font-semibold flex items-center gap-2 active:scale-95">
|
||||
<i data-lucide="plus" class="w-4 h-4"></i>
|
||||
New project
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Content Area -->
|
||||
<div class="flex-1 overflow-y-auto px-8 pb-8">
|
||||
|
||||
<!-- Controls Row (Select all + Sort) -->
|
||||
<div id="controls-row" class="controls-row group mb-4 flex items-center gap-3 text-slate-700">
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="flex items-center gap-3 text-sm font-medium cursor-pointer select-none p-1 rounded-lg">
|
||||
<input id="select-all" type="checkbox" class="custom-checkbox controls-select size-5" />
|
||||
<span class="text-slate-500 group-hover:text-slate-700">Select all</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="h-4 w-px bg-slate-200 mx-2"></div>
|
||||
<button type="button" class="inline-flex items-center gap-1.5 rounded-lg p-1 text-sm font-medium text-slate-500 hover:text-slate-900">
|
||||
Name
|
||||
<i data-lucide="arrow-down" class="size-3.5"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Grid -->
|
||||
<div id="projects-grid" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8">
|
||||
|
||||
<!-- Project Card 1 -->
|
||||
<div class="project-card group rounded-[24px] overflow-hidden relative h-auto flex flex-col">
|
||||
|
||||
<!-- Selection Checkbox -->
|
||||
<div class="project-select-wrapper absolute top-4 left-4 z-20">
|
||||
<input type="checkbox" class="custom-checkbox project-select w-6 h-6 cursor-pointer shadow-md" onchange="toggleSelection(this)">
|
||||
</div>
|
||||
|
||||
<!-- Menu -->
|
||||
<div class="project-menu absolute top-4 right-4 z-20 opacity-0 group-hover:opacity-100">
|
||||
<button class="p-2 bg-white/90 backdrop-blur-sm rounded-full shadow-sm hover:bg-white text-slate-600 hover:text-[#00A3FF]">
|
||||
<i data-lucide="more-horizontal" class="w-4 h-4"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Thumbnail -->
|
||||
<div class="aspect-video bg-slate-50 relative overflow-hidden m-1 rounded-lg">
|
||||
<img src="C:\Users\heart\Downloads\download.jpg" class="w-full h-full object-cover" alt="Project">
|
||||
<div class="absolute bottom-2 right-2 bg-black/60 text-white text-[11px] font-semibold px-2 py-1 rounded-lg">
|
||||
02:14
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info -->
|
||||
<div class="px-2 pb-5 pt-3 flex-1 flex flex-col justify-between">
|
||||
<div>
|
||||
<h3 class="font-semibold text-slate-900 truncate text-[15px]">Summer Vlog 2025</h3>
|
||||
<div class="flex items-center gap-2 mt-2 text-slate-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="size-4" width="24" height="24" color="currentColor" fill="none" stroke="currentColor" stroke-width="1.5" className="">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="M12 8V12L14 14" />
|
||||
</svg>
|
||||
<p class="text-xs font-medium">Created Jan 20, 2026</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Project Card 2 -->
|
||||
<div class="project-card group rounded-[24px] overflow-hidden relative h-auto flex flex-col">
|
||||
<!-- Selection Checkbox -->
|
||||
<div class="project-select-wrapper absolute top-4 left-4 z-20">
|
||||
<input type="checkbox" class="custom-checkbox project-select w-6 h-6 cursor-pointer shadow-md" onchange="toggleSelection(this)">
|
||||
</div>
|
||||
<!-- Menu -->
|
||||
<div class="project-menu absolute top-4 right-4 z-20 opacity-0 group-hover:opacity-100">
|
||||
<button class="p-2 bg-white/90 backdrop-blur-sm rounded-full shadow-sm hover:bg-white text-slate-600 hover:text-[#00A3FF]">
|
||||
<i data-lucide="more-horizontal" class="w-4 h-4"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="aspect-video bg-slate-50 relative overflow-hidden m-1 rounded-lg">
|
||||
<img src="C:\Users\heart\Downloads\download.jpg" class="w-full h-full object-cover" alt="Project">
|
||||
<div class="absolute bottom-2 right-2 bg-black/60 text-white text-[11px] font-semibold px-2 py-1 rounded-lg">
|
||||
00:45
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-2 pb-5 pt-3 flex-1 flex flex-col justify-between">
|
||||
<div>
|
||||
<h3 class="font-semibold text-slate-900 truncate text-[15px]">Product Launch Teaser</h3>
|
||||
<div class="flex items-center gap-2 mt-2 text-slate-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="size-4" width="24" height="24" color="currentColor" fill="none" stroke="currentColor" stroke-width="1.5" className="">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="M12 8V12L14 14" />
|
||||
</svg>
|
||||
<p class="text-xs font-medium">Created Jan 20, 2026</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Project Card 3 -->
|
||||
<div class="project-card group rounded-[24px] overflow-hidden relative h-auto flex flex-col">
|
||||
<!-- Selection Checkbox -->
|
||||
<div class="project-select-wrapper absolute top-4 left-4 z-20">
|
||||
<input type="checkbox" class="custom-checkbox project-select w-6 h-6 cursor-pointer shadow-md" onchange="toggleSelection(this)">
|
||||
</div>
|
||||
<!-- Menu -->
|
||||
<div class="project-menu absolute top-4 right-4 z-20 opacity-0 group-hover:opacity-100">
|
||||
<button class="p-2 bg-white/90 backdrop-blur-sm rounded-full shadow-sm hover:bg-white text-slate-600 hover:text-[#00A3FF]">
|
||||
<i data-lucide="more-horizontal" class="w-4 h-4"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="aspect-video bg-slate-50 relative overflow-hidden m-1 rounded-lg">
|
||||
<img src="C:\Users\heart\Downloads\download.jpg" class="w-full h-full object-cover" alt="Project">
|
||||
<div class="absolute bottom-2 right-2 bg-black/60 text-white text-[11px] font-semibold px-2 py-1 rounded-lg">
|
||||
03:30
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-2 pb-5 pt-3 flex-1 flex flex-col justify-between">
|
||||
<div>
|
||||
<h3 class="font-semibold text-slate-900 truncate text-[15px]">Podcast Ep. 4</h3>
|
||||
<div class="flex items-center gap-2 mt-2 text-slate-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="size-4" width="24" height="24" color="currentColor" fill="none" stroke="currentColor" stroke-width="1.5" className="">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="M12 8V12L14 14" />
|
||||
</svg>
|
||||
<p class="text-xs font-medium">Created Jan 18, 2026</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div id="empty-state" class="hidden flex flex-col items-center justify-center py-24 text-center">
|
||||
<div class="w-16 h-16 bg-slate-50 rounded-2xl flex items-center justify-center mb-6 ring-1 ring-slate-900/5">
|
||||
<i data-lucide="search" class="w-8 h-8 text-slate-400"></i>
|
||||
</div>
|
||||
<h3 class="text-slate-900 font-semibold text-lg mb-2">No projects found</h3>
|
||||
<p class="text-slate-500 text-sm max-w-[280px] mx-auto leading-relaxed mb-8">
|
||||
We couldn't find any projects matching your search. Try adjusting your terms.
|
||||
</p>
|
||||
<button onclick="clearSearch()" class="inline-flex items-center justify-center px-6 py-2.5 text-sm font-medium text-slate-700 bg-white border border-slate-200 rounded-full hover:bg-slate-50 hover:text-slate-900 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-200">
|
||||
Clear search
|
||||
</button>
|
||||
</div>
|
||||
<!-- Header -->
|
||||
<header class="h-20 flex items-center justify-between px-8 flex-shrink-0">
|
||||
<div class="flex items-center gap-4">
|
||||
<h1 class="text-xl font-bold text-slate-900">All projects</h1>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="relative group">
|
||||
<i
|
||||
data-lucide="search"
|
||||
class="absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400"
|
||||
></i>
|
||||
<input
|
||||
id="search-input"
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
class="w-64 bg-white border border-slate-200 text-slate-900 text-sm rounded-full pl-10 pr-4 py-2.5 focus:outline-none focus:border-[#00A3FF] focus:ring-4 focus:ring-blue-500/10 placeholder:text-slate-400 shadow-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex bg-white rounded-full border border-slate-200 p-1 shadow-sm"
|
||||
>
|
||||
<button
|
||||
class="p-2 rounded-full bg-slate-100 text-slate-900 shadow-sm"
|
||||
>
|
||||
<i data-lucide="layout-grid" class="w-4 h-4"></i>
|
||||
</button>
|
||||
<button
|
||||
class="p-2 rounded-full text-slate-400 hover:text-slate-600"
|
||||
>
|
||||
<i data-lucide="list" class="w-4 h-4"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="bg-[#00A3FF] hover:bg-[#008BE0] text-white px-5 py-2.5 rounded-full text-sm font-semibold flex items-center gap-2 active:scale-95"
|
||||
>
|
||||
<i data-lucide="plus" class="w-4 h-4"></i>
|
||||
New project
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Content Area -->
|
||||
<div class="flex-1 overflow-y-auto px-8 pb-8">
|
||||
<!-- Controls Row (Select all + Sort) -->
|
||||
<div
|
||||
id="controls-row"
|
||||
class="controls-row group mb-4 flex items-center gap-3 text-slate-700"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<label
|
||||
class="flex items-center gap-3 text-sm font-medium cursor-pointer select-none p-1 rounded-lg"
|
||||
>
|
||||
<input
|
||||
id="select-all"
|
||||
type="checkbox"
|
||||
class="custom-checkbox controls-select size-5"
|
||||
/>
|
||||
<span class="text-slate-500 group-hover:text-slate-700"
|
||||
>Select all</span
|
||||
>
|
||||
</label>
|
||||
</div>
|
||||
<div class="h-4 w-px bg-slate-200 mx-2"></div>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center gap-1.5 rounded-lg p-1 text-sm font-medium text-slate-500 hover:text-slate-900"
|
||||
>
|
||||
Name
|
||||
<i data-lucide="arrow-down" class="size-3.5"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Grid -->
|
||||
<div
|
||||
id="projects-grid"
|
||||
class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8"
|
||||
>
|
||||
<!-- Project Card 1 -->
|
||||
<div
|
||||
class="project-card group rounded-[24px] overflow-hidden relative h-auto flex flex-col"
|
||||
>
|
||||
<!-- Selection Checkbox -->
|
||||
<div class="project-select-wrapper absolute top-4 left-4 z-20">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="custom-checkbox project-select w-6 h-6 cursor-pointer shadow-md"
|
||||
onchange="toggleSelection(this)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Menu -->
|
||||
<div
|
||||
class="project-menu absolute top-4 right-4 z-20 opacity-0 group-hover:opacity-100"
|
||||
>
|
||||
<button
|
||||
class="p-2 bg-white/90 backdrop-blur-sm rounded-full shadow-sm hover:bg-white text-slate-600 hover:text-[#00A3FF]"
|
||||
>
|
||||
<i data-lucide="more-horizontal" class="w-4 h-4"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Thumbnail -->
|
||||
<div
|
||||
class="aspect-video bg-slate-50 relative overflow-hidden m-1 rounded-lg"
|
||||
>
|
||||
<img
|
||||
src="C:\Users\heart\Downloads\download.jpg"
|
||||
class="w-full h-full object-cover"
|
||||
alt="Project"
|
||||
/>
|
||||
<div
|
||||
class="absolute bottom-2 right-2 bg-black/60 text-white text-[11px] font-semibold px-2 py-1 rounded-lg"
|
||||
>
|
||||
02:14
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info -->
|
||||
<div class="px-2 pb-5 pt-3 flex-1 flex flex-col justify-between">
|
||||
<div>
|
||||
<h3 class="font-semibold text-slate-900 truncate text-[15px]">
|
||||
Summer Vlog 2025
|
||||
</h3>
|
||||
<div class="flex items-center gap-2 mt-2 text-slate-400">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
class="size-4"
|
||||
width="24"
|
||||
height="24"
|
||||
color="currentColor"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
className=""
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="M12 8V12L14 14" />
|
||||
</svg>
|
||||
<p class="text-xs font-medium">Created Jan 20, 2026</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Project Card 2 -->
|
||||
<div
|
||||
class="project-card group rounded-[24px] overflow-hidden relative h-auto flex flex-col"
|
||||
>
|
||||
<!-- Selection Checkbox -->
|
||||
<div class="project-select-wrapper absolute top-4 left-4 z-20">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="custom-checkbox project-select w-6 h-6 cursor-pointer shadow-md"
|
||||
onchange="toggleSelection(this)"
|
||||
/>
|
||||
</div>
|
||||
<!-- Menu -->
|
||||
<div
|
||||
class="project-menu absolute top-4 right-4 z-20 opacity-0 group-hover:opacity-100"
|
||||
>
|
||||
<button
|
||||
class="p-2 bg-white/90 backdrop-blur-sm rounded-full shadow-sm hover:bg-white text-slate-600 hover:text-[#00A3FF]"
|
||||
>
|
||||
<i data-lucide="more-horizontal" class="w-4 h-4"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="aspect-video bg-slate-50 relative overflow-hidden m-1 rounded-lg"
|
||||
>
|
||||
<img
|
||||
src="C:\Users\heart\Downloads\download.jpg"
|
||||
class="w-full h-full object-cover"
|
||||
alt="Project"
|
||||
/>
|
||||
<div
|
||||
class="absolute bottom-2 right-2 bg-black/60 text-white text-[11px] font-semibold px-2 py-1 rounded-lg"
|
||||
>
|
||||
00:45
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-2 pb-5 pt-3 flex-1 flex flex-col justify-between">
|
||||
<div>
|
||||
<h3 class="font-semibold text-slate-900 truncate text-[15px]">
|
||||
Product Launch Teaser
|
||||
</h3>
|
||||
<div class="flex items-center gap-2 mt-2 text-slate-400">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
class="size-4"
|
||||
width="24"
|
||||
height="24"
|
||||
color="currentColor"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
className=""
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="M12 8V12L14 14" />
|
||||
</svg>
|
||||
<p class="text-xs font-medium">Created Jan 20, 2026</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Project Card 3 -->
|
||||
<div
|
||||
class="project-card group rounded-[24px] overflow-hidden relative h-auto flex flex-col"
|
||||
>
|
||||
<!-- Selection Checkbox -->
|
||||
<div class="project-select-wrapper absolute top-4 left-4 z-20">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="custom-checkbox project-select w-6 h-6 cursor-pointer shadow-md"
|
||||
onchange="toggleSelection(this)"
|
||||
/>
|
||||
</div>
|
||||
<!-- Menu -->
|
||||
<div
|
||||
class="project-menu absolute top-4 right-4 z-20 opacity-0 group-hover:opacity-100"
|
||||
>
|
||||
<button
|
||||
class="p-2 bg-white/90 backdrop-blur-sm rounded-full shadow-sm hover:bg-white text-slate-600 hover:text-[#00A3FF]"
|
||||
>
|
||||
<i data-lucide="more-horizontal" class="w-4 h-4"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="aspect-video bg-slate-50 relative overflow-hidden m-1 rounded-lg"
|
||||
>
|
||||
<img
|
||||
src="C:\Users\heart\Downloads\download.jpg"
|
||||
class="w-full h-full object-cover"
|
||||
alt="Project"
|
||||
/>
|
||||
<div
|
||||
class="absolute bottom-2 right-2 bg-black/60 text-white text-[11px] font-semibold px-2 py-1 rounded-lg"
|
||||
>
|
||||
03:30
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-2 pb-5 pt-3 flex-1 flex flex-col justify-between">
|
||||
<div>
|
||||
<h3 class="font-semibold text-slate-900 truncate text-[15px]">
|
||||
Podcast Ep. 4
|
||||
</h3>
|
||||
<div class="flex items-center gap-2 mt-2 text-slate-400">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
class="size-4"
|
||||
width="24"
|
||||
height="24"
|
||||
color="currentColor"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
className=""
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="M12 8V12L14 14" />
|
||||
</svg>
|
||||
<p class="text-xs font-medium">Created Jan 18, 2026</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div
|
||||
id="empty-state"
|
||||
class="hidden flex flex-col items-center justify-center py-24 text-center"
|
||||
>
|
||||
<div
|
||||
class="w-16 h-16 bg-slate-50 rounded-2xl flex items-center justify-center mb-6 ring-1 ring-slate-900/5"
|
||||
>
|
||||
<i data-lucide="search" class="w-8 h-8 text-slate-400"></i>
|
||||
</div>
|
||||
<h3 class="text-slate-900 font-semibold text-lg mb-2">
|
||||
No projects found
|
||||
</h3>
|
||||
<p
|
||||
class="text-slate-500 text-sm max-w-[280px] mx-auto leading-relaxed mb-8"
|
||||
>
|
||||
We couldn't find any projects matching your search. Try adjusting
|
||||
your terms.
|
||||
</p>
|
||||
<button
|
||||
onclick="clearSearch()"
|
||||
class="inline-flex items-center justify-center px-6 py-2.5 text-sm font-medium text-slate-700 bg-white border border-slate-200 rounded-full hover:bg-slate-50 hover:text-slate-900 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-200"
|
||||
>
|
||||
Clear search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
lucide.createIcons();
|
||||
|
||||
const selectedProjectIds = new Set();
|
||||
lucide.createIcons();
|
||||
|
||||
function getProjectId({ card, index }) {
|
||||
return card.getAttribute('data-project-id') ?? `project-${index}`;
|
||||
const selectedProjectIds = new Set();
|
||||
|
||||
function getProjectId({ card, index }) {
|
||||
return card.getAttribute("data-project-id") ?? `project-${index}`;
|
||||
}
|
||||
|
||||
function updateControlsSelectionState() {
|
||||
const controlsRow = document.getElementById("controls-row");
|
||||
const selectAll = document.getElementById("select-all");
|
||||
const projectCheckboxes = document.querySelectorAll(".project-select");
|
||||
if (!controlsRow || !selectAll) {
|
||||
return;
|
||||
}
|
||||
const selectedCount = selectedProjectIds.size;
|
||||
document.body.classList.toggle("multi-select", selectedCount > 1);
|
||||
controlsRow.classList.toggle("has-selection", selectedCount > 0);
|
||||
const totalCount = projectCheckboxes.length;
|
||||
selectAll.checked = selectedCount > 0 && selectedCount === totalCount;
|
||||
selectAll.indeterminate =
|
||||
selectedCount > 0 && selectedCount < totalCount;
|
||||
}
|
||||
|
||||
|
||||
function updateControlsSelectionState() {
|
||||
const controlsRow = document.getElementById('controls-row');
|
||||
const selectAll = document.getElementById('select-all');
|
||||
const projectCheckboxes = document.querySelectorAll('.project-select');
|
||||
if (!controlsRow || !selectAll) {
|
||||
return;
|
||||
}
|
||||
const selectedCount = selectedProjectIds.size;
|
||||
document.body.classList.toggle('multi-select', selectedCount > 1);
|
||||
controlsRow.classList.toggle('has-selection', selectedCount > 0);
|
||||
const totalCount = projectCheckboxes.length;
|
||||
selectAll.checked = selectedCount > 0 && selectedCount === totalCount;
|
||||
selectAll.indeterminate = selectedCount > 0 && selectedCount < totalCount;
|
||||
function syncSelectionState() {
|
||||
const projectCards = Array.from(
|
||||
document.querySelectorAll(".project-card"),
|
||||
);
|
||||
for (const [index, card] of projectCards.entries()) {
|
||||
const checkbox = card.querySelector(".project-select");
|
||||
if (!checkbox) {
|
||||
continue;
|
||||
}
|
||||
const projectId = getProjectId({ card, index });
|
||||
const isSelected = selectedProjectIds.has(projectId);
|
||||
checkbox.checked = isSelected;
|
||||
}
|
||||
updateControlsSelectionState();
|
||||
}
|
||||
|
||||
function syncSelectionState() {
|
||||
const projectCards = Array.from(document.querySelectorAll('.project-card'));
|
||||
for (const [index, card] of projectCards.entries()) {
|
||||
const checkbox = card.querySelector('.project-select');
|
||||
if (!checkbox) {
|
||||
continue;
|
||||
}
|
||||
const projectId = getProjectId({ card, index });
|
||||
const isSelected = selectedProjectIds.has(projectId);
|
||||
checkbox.checked = isSelected;
|
||||
}
|
||||
updateControlsSelectionState();
|
||||
function toggleSelection(checkbox) {
|
||||
const card = checkbox.closest(".project-card");
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
|
||||
function toggleSelection(checkbox) {
|
||||
const card = checkbox.closest('.project-card');
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
const projectCards = Array.from(document.querySelectorAll('.project-card'));
|
||||
const projectId = getProjectId({
|
||||
card,
|
||||
index: projectCards.indexOf(card),
|
||||
});
|
||||
if (checkbox.checked) {
|
||||
selectedProjectIds.add(projectId);
|
||||
} else {
|
||||
selectedProjectIds.delete(projectId);
|
||||
}
|
||||
syncSelectionState();
|
||||
}
|
||||
|
||||
function toggleSelectAll({ isSelected }) {
|
||||
const projectCards = Array.from(document.querySelectorAll('.project-card'));
|
||||
if (!isSelected) {
|
||||
selectedProjectIds.clear();
|
||||
syncSelectionState();
|
||||
return;
|
||||
}
|
||||
for (const [index, card] of projectCards.entries()) {
|
||||
selectedProjectIds.add(getProjectId({ card, index }));
|
||||
}
|
||||
syncSelectionState();
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const selectAll = document.getElementById('select-all');
|
||||
if (selectAll) {
|
||||
selectAll.addEventListener('change', () => {
|
||||
toggleSelectAll({ isSelected: selectAll.checked });
|
||||
});
|
||||
}
|
||||
syncSelectionState();
|
||||
|
||||
// Search functionality
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const emptyState = document.getElementById('empty-state');
|
||||
const projectsGrid = document.getElementById('projects-grid');
|
||||
|
||||
if (searchInput) {
|
||||
searchInput.addEventListener('input', (e) => {
|
||||
const query = e.target.value.toLowerCase().trim();
|
||||
const cards = document.querySelectorAll('.project-card');
|
||||
let hasVisibleProjects = false;
|
||||
|
||||
cards.forEach(card => {
|
||||
const titleElement = card.querySelector('h3');
|
||||
if (!titleElement) return;
|
||||
|
||||
const title = titleElement.textContent.toLowerCase();
|
||||
const isMatch = title.includes(query);
|
||||
|
||||
if (isMatch) {
|
||||
card.classList.remove('hidden');
|
||||
hasVisibleProjects = true;
|
||||
} else {
|
||||
card.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle empty state
|
||||
if (hasVisibleProjects) {
|
||||
emptyState.classList.add('hidden');
|
||||
projectsGrid.classList.remove('hidden');
|
||||
} else {
|
||||
emptyState.classList.remove('hidden');
|
||||
projectsGrid.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Re-initialize icons for new elements (if any dynamic ones were added, though here we just hide/show)
|
||||
// But importantly, the empty state icon needs to be rendered if it wasn't visible initially
|
||||
lucide.createIcons();
|
||||
});
|
||||
}
|
||||
|
||||
// Expose clearSearch globally
|
||||
window.clearSearch = function() {
|
||||
if (searchInput) {
|
||||
searchInput.value = '';
|
||||
searchInput.dispatchEvent(new Event('input'));
|
||||
searchInput.focus();
|
||||
}
|
||||
};
|
||||
const projectCards = Array.from(
|
||||
document.querySelectorAll(".project-card"),
|
||||
);
|
||||
const projectId = getProjectId({
|
||||
card,
|
||||
index: projectCards.indexOf(card),
|
||||
});
|
||||
if (checkbox.checked) {
|
||||
selectedProjectIds.add(projectId);
|
||||
} else {
|
||||
selectedProjectIds.delete(projectId);
|
||||
}
|
||||
syncSelectionState();
|
||||
}
|
||||
|
||||
function toggleSelectAll({ isSelected }) {
|
||||
const projectCards = Array.from(
|
||||
document.querySelectorAll(".project-card"),
|
||||
);
|
||||
if (!isSelected) {
|
||||
selectedProjectIds.clear();
|
||||
syncSelectionState();
|
||||
return;
|
||||
}
|
||||
for (const [index, card] of projectCards.entries()) {
|
||||
selectedProjectIds.add(getProjectId({ card, index }));
|
||||
}
|
||||
syncSelectionState();
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const selectAll = document.getElementById("select-all");
|
||||
if (selectAll) {
|
||||
selectAll.addEventListener("change", () => {
|
||||
toggleSelectAll({ isSelected: selectAll.checked });
|
||||
});
|
||||
}
|
||||
syncSelectionState();
|
||||
|
||||
// Search functionality
|
||||
const searchInput = document.getElementById("search-input");
|
||||
const emptyState = document.getElementById("empty-state");
|
||||
const projectsGrid = document.getElementById("projects-grid");
|
||||
|
||||
if (searchInput) {
|
||||
searchInput.addEventListener("input", (e) => {
|
||||
const query = e.target.value.toLowerCase().trim();
|
||||
const cards = document.querySelectorAll(".project-card");
|
||||
let hasVisibleProjects = false;
|
||||
|
||||
cards.forEach((card) => {
|
||||
const titleElement = card.querySelector("h3");
|
||||
if (!titleElement) return;
|
||||
|
||||
const title = titleElement.textContent.toLowerCase();
|
||||
const isMatch = title.includes(query);
|
||||
|
||||
if (isMatch) {
|
||||
card.classList.remove("hidden");
|
||||
hasVisibleProjects = true;
|
||||
} else {
|
||||
card.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle empty state
|
||||
if (hasVisibleProjects) {
|
||||
emptyState.classList.add("hidden");
|
||||
projectsGrid.classList.remove("hidden");
|
||||
} else {
|
||||
emptyState.classList.remove("hidden");
|
||||
projectsGrid.classList.add("hidden");
|
||||
}
|
||||
|
||||
// Re-initialize icons for new elements (if any dynamic ones were added, though here we just hide/show)
|
||||
// But importantly, the empty state icon needs to be rendered if it wasn't visible initially
|
||||
lucide.createIcons();
|
||||
});
|
||||
}
|
||||
|
||||
// Expose clearSearch globally
|
||||
window.clearSearch = function () {
|
||||
if (searchInput) {
|
||||
searchInput.value = "";
|
||||
searchInput.dispatchEvent(new Event("input"));
|
||||
searchInput.focus();
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
import cv2
|
||||
import sys
|
||||
|
||||
MAX_WIDTH = 1200
|
||||
MAX_HEIGHT = 800
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python selector.py <image_path>")
|
||||
sys.exit(1)
|
||||
|
||||
image_path = sys.argv[1]
|
||||
original = cv2.imread(image_path)
|
||||
|
||||
if original is None:
|
||||
print(f"Could not load image: {image_path}")
|
||||
sys.exit(1)
|
||||
|
||||
orig_h, orig_w = original.shape[:2]
|
||||
|
||||
# scale down if too big
|
||||
scale = min(MAX_WIDTH / orig_w, MAX_HEIGHT / orig_h, 1.0)
|
||||
if scale < 1.0:
|
||||
new_w, new_h = int(orig_w * scale), int(orig_h * scale)
|
||||
img = cv2.resize(original, (new_w, new_h))
|
||||
print(f"📐 Scaled {orig_w}x{orig_h} → {new_w}x{new_h} (scale: {scale:.2f})")
|
||||
else:
|
||||
img = original
|
||||
scale = 1.0
|
||||
|
||||
points = []
|
||||
|
||||
def to_original(x, y):
|
||||
return int(x / scale), int(y / scale)
|
||||
|
||||
def mouse_callback(event, x, y, flags, param):
|
||||
if event == cv2.EVENT_LBUTTONDOWN:
|
||||
orig_x, orig_y = to_original(x, y)
|
||||
points.append((orig_x, orig_y))
|
||||
print(f"\n📍 Point {len(points)}: X={orig_x}, Y={orig_y}")
|
||||
cv2.circle(param['display'], (x, y), 5, (0, 255, 0), -1)
|
||||
cv2.putText(param['display'], f"({orig_x},{orig_y})", (x+10, y),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
|
||||
cv2.imshow("Image Selector", param['display'])
|
||||
|
||||
display = img.copy()
|
||||
cv2.namedWindow("Image Selector")
|
||||
cv2.setMouseCallback("Image Selector", mouse_callback, {'display': display})
|
||||
|
||||
print("\n=== SAM2 Coordinate Selector ===")
|
||||
print(f"Original size: {orig_w}x{orig_h}")
|
||||
print("Left-click: Add point prompt")
|
||||
print("Press 'b': Draw bounding box")
|
||||
print("Press 'r': Reset")
|
||||
print("Press 'q': Quit\n")
|
||||
|
||||
while True:
|
||||
cv2.imshow("Image Selector", display)
|
||||
key = cv2.waitKey(1) & 0xFF
|
||||
|
||||
if key == ord('q'):
|
||||
break
|
||||
elif key == ord('b'):
|
||||
print("\nDraw a box (click and drag), then press ENTER or SPACE")
|
||||
roi = cv2.selectROI("Image Selector", img, fromCenter=False, showCrosshair=True)
|
||||
if roi != (0, 0, 0, 0):
|
||||
x, y, w, h = roi
|
||||
# convert to original coords
|
||||
x_min, y_min = to_original(x, y)
|
||||
x_max, y_max = to_original(x + w, y + h)
|
||||
print(f"\n📦 Box Prompts:")
|
||||
print(f" X Min: {x_min}")
|
||||
print(f" Y Min: {y_min}")
|
||||
print(f" X Max: {x_max}")
|
||||
print(f" Y Max: {y_max}")
|
||||
# redraw
|
||||
display = img.copy()
|
||||
cv2.rectangle(display, (x, y), (x + w, y + h), (255, 0, 0), 2)
|
||||
for orig_px, orig_py in points:
|
||||
px, py = int(orig_px * scale), int(orig_py * scale)
|
||||
cv2.circle(display, (px, py), 5, (0, 255, 0), -1)
|
||||
elif key == ord('r'):
|
||||
display = img.copy()
|
||||
points.clear()
|
||||
print("\n🔄 Reset")
|
||||
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
if points:
|
||||
print("\n=== Final Point Prompts ===")
|
||||
for i, (x, y) in enumerate(points, 1):
|
||||
print(f"Point {i}: X={x}, Y={y}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue