Spaces:
Sleeping
Sleeping
DevelopedBy-Siva commited on
Commit ·
3c93545
1
Parent(s): 83cbd12
fix ui
Browse files- extension/manifest.json +5 -1
- extension/sidebar/app.js +121 -0
- extension/sidebar/app.jsx +0 -34
- extension/sidebar/components/{CategorizationScreen.jsx → CategorizationScreen.js} +3 -6
- extension/sidebar/components/{ConnectSheet.jsx → ConnectSheet.js} +0 -0
- extension/sidebar/components/{CustomTaskInput.jsx → CustomTaskInput.js} +0 -0
- extension/sidebar/components/{DashboardScreen.jsx → DashboardScreen.js} +1 -1
- extension/sidebar/components/{DeployingScreen.jsx → DeployingScreen.js} +0 -0
- extension/sidebar/components/{EscalationPanel.jsx → EscalationPanel.js} +0 -0
- extension/sidebar/components/{FileUpload.jsx → FileUpload.js} +0 -0
- extension/sidebar/components/OnboardingScreen.js +27 -0
- extension/sidebar/components/OnboardingScreen.jsx +0 -10
- extension/sidebar/components/{StatusBadge.jsx → StatusBadge.js} +0 -0
- extension/sidebar/components/{TaskCategoryGroup.jsx → TaskCategoryGroup.js} +0 -0
- extension/sidebar/components/{WorkflowCard.jsx → WorkflowCard.js} +0 -0
- extension/sidebar/components/{WorkflowPicker.jsx → WorkflowPicker.js} +1 -1
- extension/sidebar/hooks/useApi.js +6 -1
- extension/sidebar/index.html +1 -1
- extension/sidebar/styles/sidebar.css +24 -0
extension/manifest.json
CHANGED
|
@@ -4,7 +4,11 @@
|
|
| 4 |
"version": "0.1.0",
|
| 5 |
"description": "AI workflow builder for Gmail-based small businesses.",
|
| 6 |
"permissions": ["storage", "activeTab", "scripting"],
|
| 7 |
-
"host_permissions": [
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
"background": {
|
| 9 |
"service_worker": "background.js"
|
| 10 |
},
|
|
|
|
| 4 |
"version": "0.1.0",
|
| 5 |
"description": "AI workflow builder for Gmail-based small businesses.",
|
| 6 |
"permissions": ["storage", "activeTab", "scripting"],
|
| 7 |
+
"host_permissions": [
|
| 8 |
+
"https://mail.google.com/*",
|
| 9 |
+
"http://localhost:8000/*",
|
| 10 |
+
"https://*.hf.space/*"
|
| 11 |
+
],
|
| 12 |
"background": {
|
| 13 |
"service_worker": "background.js"
|
| 14 |
},
|
extension/sidebar/app.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { OnboardingScreen } from "./components/OnboardingScreen.js";
|
| 2 |
+
import { CategorizationScreen } from "./components/CategorizationScreen.js";
|
| 3 |
+
import { CustomTaskInput } from "./components/CustomTaskInput.js";
|
| 4 |
+
import { WorkflowPicker } from "./components/WorkflowPicker.js";
|
| 5 |
+
import { ConnectSheet } from "./components/ConnectSheet.js";
|
| 6 |
+
import { FileUpload } from "./components/FileUpload.js";
|
| 7 |
+
import { DeployingScreen } from "./components/DeployingScreen.js";
|
| 8 |
+
import { DashboardScreen } from "./components/DashboardScreen.js";
|
| 9 |
+
import { EscalationPanel } from "./components/EscalationPanel.js";
|
| 10 |
+
import { apiRequest } from "./hooks/useApi.js";
|
| 11 |
+
import { TaskCategoryGroup } from "./components/TaskCategoryGroup.js";
|
| 12 |
+
|
| 13 |
+
const root = document.getElementById("root");
|
| 14 |
+
const defaultDescription =
|
| 15 |
+
"I run an apple orchard. Customers email me orders, I check inventory in my Google Sheet, reply with pickup details, and every Friday I count weekly orders.";
|
| 16 |
+
|
| 17 |
+
const steps = [
|
| 18 |
+
OnboardingScreen(),
|
| 19 |
+
CategorizationScreen(),
|
| 20 |
+
CustomTaskInput(),
|
| 21 |
+
WorkflowPicker(),
|
| 22 |
+
ConnectSheet(),
|
| 23 |
+
FileUpload(),
|
| 24 |
+
DeployingScreen(),
|
| 25 |
+
DashboardScreen(),
|
| 26 |
+
EscalationPanel()
|
| 27 |
+
];
|
| 28 |
+
|
| 29 |
+
root.innerHTML = `
|
| 30 |
+
<div class="sidebar-shell">
|
| 31 |
+
<header class="hero">
|
| 32 |
+
<p class="eyebrow">FlowPilot</p>
|
| 33 |
+
<h1>Build inbox automations without leaving Gmail</h1>
|
| 34 |
+
<p class="lede">Describe the business, choose workflows, and let the backend deploy them.</p>
|
| 35 |
+
</header>
|
| 36 |
+
<main class="screen-stack">${steps.join("")}</main>
|
| 37 |
+
</div>
|
| 38 |
+
`;
|
| 39 |
+
|
| 40 |
+
const backendUrlInput = document.getElementById("flowpilot-backend-url");
|
| 41 |
+
const saveUrlButton = document.getElementById("flowpilot-save-url");
|
| 42 |
+
const descriptionInput = document.getElementById("flowpilot-business-description");
|
| 43 |
+
const analyzeButton = document.getElementById("flowpilot-analyze-button");
|
| 44 |
+
const onboardingStatus = document.getElementById("flowpilot-onboarding-status");
|
| 45 |
+
const summary = document.getElementById("flowpilot-summary");
|
| 46 |
+
const categories = document.getElementById("flowpilot-categories");
|
| 47 |
+
|
| 48 |
+
init();
|
| 49 |
+
|
| 50 |
+
async function init() {
|
| 51 |
+
const stored = await chrome.storage.local.get("flowpilotBackendUrl");
|
| 52 |
+
backendUrlInput.value = stored.flowpilotBackendUrl || "https://technophyle-flow-pilot.hf.space/api";
|
| 53 |
+
descriptionInput.value = defaultDescription;
|
| 54 |
+
|
| 55 |
+
saveUrlButton.addEventListener("click", saveBackendUrl);
|
| 56 |
+
analyzeButton.addEventListener("click", analyzeBusiness);
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
async function saveBackendUrl() {
|
| 60 |
+
const nextUrl = backendUrlInput.value.trim().replace(/\/$/, "");
|
| 61 |
+
if (!nextUrl) {
|
| 62 |
+
setStatus("Enter a backend URL ending in /api.", true);
|
| 63 |
+
return;
|
| 64 |
+
}
|
| 65 |
+
await chrome.storage.local.set({ flowpilotBackendUrl: nextUrl });
|
| 66 |
+
setStatus(`Backend saved: ${nextUrl}`);
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
async function analyzeBusiness() {
|
| 70 |
+
const description = descriptionInput.value.trim();
|
| 71 |
+
if (!description) {
|
| 72 |
+
setStatus("Add a short business description before running analysis.", true);
|
| 73 |
+
return;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
analyzeButton.disabled = true;
|
| 77 |
+
analyzeButton.textContent = "Analyzing...";
|
| 78 |
+
setStatus("Running analysis against the configured backend.");
|
| 79 |
+
|
| 80 |
+
try {
|
| 81 |
+
await saveBackendUrl();
|
| 82 |
+
const payload = await apiRequest("/analyze", {
|
| 83 |
+
method: "POST",
|
| 84 |
+
body: JSON.stringify({
|
| 85 |
+
owner_id: "extension-demo-owner",
|
| 86 |
+
owner_email: "owner@example.com",
|
| 87 |
+
description
|
| 88 |
+
})
|
| 89 |
+
});
|
| 90 |
+
renderAnalysis(payload);
|
| 91 |
+
setStatus("Analysis loaded into the extension.");
|
| 92 |
+
} catch (error) {
|
| 93 |
+
setStatus(error.message || "Could not reach the backend.", true);
|
| 94 |
+
} finally {
|
| 95 |
+
analyzeButton.disabled = false;
|
| 96 |
+
analyzeButton.textContent = "Analyze My Process";
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
function renderAnalysis(payload) {
|
| 101 |
+
summary.textContent = payload.summary || "Analysis complete.";
|
| 102 |
+
categories.innerHTML = [
|
| 103 |
+
TaskCategoryGroup(
|
| 104 |
+
"Can Be Fully Automated",
|
| 105 |
+
(payload.tasks?.fully_automatable || []).map((task) => task.name)
|
| 106 |
+
),
|
| 107 |
+
TaskCategoryGroup(
|
| 108 |
+
"AI-Assisted",
|
| 109 |
+
(payload.tasks?.ai_assisted || []).map((task) => task.name)
|
| 110 |
+
),
|
| 111 |
+
TaskCategoryGroup(
|
| 112 |
+
"Keep Manual",
|
| 113 |
+
(payload.tasks?.manual || []).map((task) => task.name)
|
| 114 |
+
)
|
| 115 |
+
].join("");
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
function setStatus(message, isError = false) {
|
| 119 |
+
onboardingStatus.textContent = message;
|
| 120 |
+
onboardingStatus.classList.toggle("error-copy", isError);
|
| 121 |
+
}
|
extension/sidebar/app.jsx
DELETED
|
@@ -1,34 +0,0 @@
|
|
| 1 |
-
import { OnboardingScreen } from "./components/OnboardingScreen.jsx";
|
| 2 |
-
import { CategorizationScreen } from "./components/CategorizationScreen.jsx";
|
| 3 |
-
import { CustomTaskInput } from "./components/CustomTaskInput.jsx";
|
| 4 |
-
import { WorkflowPicker } from "./components/WorkflowPicker.jsx";
|
| 5 |
-
import { ConnectSheet } from "./components/ConnectSheet.jsx";
|
| 6 |
-
import { FileUpload } from "./components/FileUpload.jsx";
|
| 7 |
-
import { DeployingScreen } from "./components/DeployingScreen.jsx";
|
| 8 |
-
import { DashboardScreen } from "./components/DashboardScreen.jsx";
|
| 9 |
-
import { EscalationPanel } from "./components/EscalationPanel.jsx";
|
| 10 |
-
|
| 11 |
-
const root = document.getElementById("root");
|
| 12 |
-
|
| 13 |
-
const steps = [
|
| 14 |
-
OnboardingScreen(),
|
| 15 |
-
CategorizationScreen(),
|
| 16 |
-
CustomTaskInput(),
|
| 17 |
-
WorkflowPicker(),
|
| 18 |
-
ConnectSheet(),
|
| 19 |
-
FileUpload(),
|
| 20 |
-
DeployingScreen(),
|
| 21 |
-
DashboardScreen(),
|
| 22 |
-
EscalationPanel()
|
| 23 |
-
];
|
| 24 |
-
|
| 25 |
-
root.innerHTML = `
|
| 26 |
-
<div class="sidebar-shell">
|
| 27 |
-
<header class="hero">
|
| 28 |
-
<p class="eyebrow">FlowPilot</p>
|
| 29 |
-
<h1>Build inbox automations without leaving Gmail</h1>
|
| 30 |
-
<p class="lede">Describe the business, choose workflows, and let the backend deploy them.</p>
|
| 31 |
-
</header>
|
| 32 |
-
<main class="screen-stack">${steps.join("")}</main>
|
| 33 |
-
</div>
|
| 34 |
-
`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/sidebar/components/{CategorizationScreen.jsx → CategorizationScreen.js}
RENAMED
|
@@ -1,13 +1,10 @@
|
|
| 1 |
-
import { TaskCategoryGroup } from "./TaskCategoryGroup.jsx";
|
| 2 |
-
|
| 3 |
export function CategorizationScreen() {
|
| 4 |
return `
|
| 5 |
-
<section class="screen-card">
|
| 6 |
<div class="step-label">2. AI Categorization</div>
|
| 7 |
<h2>Here’s what FlowPilot found</h2>
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
${TaskCategoryGroup("Keep Manual", ["Custom requests", "VIP relationship messages"])}
|
| 11 |
</section>
|
| 12 |
`;
|
| 13 |
}
|
|
|
|
|
|
|
|
|
|
| 1 |
export function CategorizationScreen() {
|
| 2 |
return `
|
| 3 |
+
<section class="screen-card" id="flowpilot-categorization-card">
|
| 4 |
<div class="step-label">2. AI Categorization</div>
|
| 5 |
<h2>Here’s what FlowPilot found</h2>
|
| 6 |
+
<p id="flowpilot-summary" class="muted">Run the analysis above to load real recommendations from the backend.</p>
|
| 7 |
+
<div id="flowpilot-categories"></div>
|
|
|
|
| 8 |
</section>
|
| 9 |
`;
|
| 10 |
}
|
extension/sidebar/components/{ConnectSheet.jsx → ConnectSheet.js}
RENAMED
|
File without changes
|
extension/sidebar/components/{CustomTaskInput.jsx → CustomTaskInput.js}
RENAMED
|
File without changes
|
extension/sidebar/components/{DashboardScreen.jsx → DashboardScreen.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import { StatusBadge } from "./StatusBadge.
|
| 2 |
|
| 3 |
export function DashboardScreen() {
|
| 4 |
return `
|
|
|
|
| 1 |
+
import { StatusBadge } from "./StatusBadge.js";
|
| 2 |
|
| 3 |
export function DashboardScreen() {
|
| 4 |
return `
|
extension/sidebar/components/{DeployingScreen.jsx → DeployingScreen.js}
RENAMED
|
File without changes
|
extension/sidebar/components/{EscalationPanel.jsx → EscalationPanel.js}
RENAMED
|
File without changes
|
extension/sidebar/components/{FileUpload.jsx → FileUpload.js}
RENAMED
|
File without changes
|
extension/sidebar/components/OnboardingScreen.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export function OnboardingScreen() {
|
| 2 |
+
return `
|
| 3 |
+
<section class="screen-card">
|
| 4 |
+
<div class="step-label">1. Describe Business</div>
|
| 5 |
+
<h2>What does your business handle every day?</h2>
|
| 6 |
+
<label class="field-label" for="flowpilot-backend-url">Backend URL</label>
|
| 7 |
+
<input
|
| 8 |
+
id="flowpilot-backend-url"
|
| 9 |
+
class="text-input"
|
| 10 |
+
placeholder="https://technophyle-flow-pilot.hf.space/api"
|
| 11 |
+
/>
|
| 12 |
+
<div class="button-row">
|
| 13 |
+
<button id="flowpilot-save-url" class="ghost-button">Save URL</button>
|
| 14 |
+
</div>
|
| 15 |
+
<label class="field-label" for="flowpilot-business-description">Business Description</label>
|
| 16 |
+
<textarea
|
| 17 |
+
id="flowpilot-business-description"
|
| 18 |
+
class="input-area"
|
| 19 |
+
placeholder="I run an apple orchard. Customers email me orders..."
|
| 20 |
+
></textarea>
|
| 21 |
+
<div class="button-row">
|
| 22 |
+
<button id="flowpilot-analyze-button" class="primary-button">Analyze My Process</button>
|
| 23 |
+
</div>
|
| 24 |
+
<p id="flowpilot-onboarding-status" class="muted status-copy">Connect the extension to your backend, then run analysis.</p>
|
| 25 |
+
</section>
|
| 26 |
+
`;
|
| 27 |
+
}
|
extension/sidebar/components/OnboardingScreen.jsx
DELETED
|
@@ -1,10 +0,0 @@
|
|
| 1 |
-
export function OnboardingScreen() {
|
| 2 |
-
return `
|
| 3 |
-
<section class="screen-card">
|
| 4 |
-
<div class="step-label">1. Describe Business</div>
|
| 5 |
-
<h2>What does your business handle every day?</h2>
|
| 6 |
-
<textarea class="input-area" placeholder="I run an apple orchard. Customers email me orders..."></textarea>
|
| 7 |
-
<button class="primary-button">Analyze My Process</button>
|
| 8 |
-
</section>
|
| 9 |
-
`;
|
| 10 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/sidebar/components/{StatusBadge.jsx → StatusBadge.js}
RENAMED
|
File without changes
|
extension/sidebar/components/{TaskCategoryGroup.jsx → TaskCategoryGroup.js}
RENAMED
|
File without changes
|
extension/sidebar/components/{WorkflowCard.jsx → WorkflowCard.js}
RENAMED
|
File without changes
|
extension/sidebar/components/{WorkflowPicker.jsx → WorkflowPicker.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import { WorkflowCard } from "./WorkflowCard.
|
| 2 |
|
| 3 |
export function WorkflowPicker() {
|
| 4 |
return `
|
|
|
|
| 1 |
+
import { WorkflowCard } from "./WorkflowCard.js";
|
| 2 |
|
| 3 |
export function WorkflowPicker() {
|
| 4 |
return `
|
extension/sidebar/hooks/useApi.js
CHANGED
|
@@ -5,5 +5,10 @@ export async function apiRequest(path, options = {}) {
|
|
| 5 |
headers: { "Content-Type": "application/json" },
|
| 6 |
...options
|
| 7 |
});
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
}
|
|
|
|
| 5 |
headers: { "Content-Type": "application/json" },
|
| 6 |
...options
|
| 7 |
});
|
| 8 |
+
const data = await response.json().catch(() => ({}));
|
| 9 |
+
if (!response.ok) {
|
| 10 |
+
const message = data?.detail || `Request failed with status ${response.status}`;
|
| 11 |
+
throw new Error(message);
|
| 12 |
+
}
|
| 13 |
+
return data;
|
| 14 |
}
|
extension/sidebar/index.html
CHANGED
|
@@ -8,6 +8,6 @@
|
|
| 8 |
</head>
|
| 9 |
<body>
|
| 10 |
<div id="root"></div>
|
| 11 |
-
<script type="module" src="./app.
|
| 12 |
</body>
|
| 13 |
</html>
|
|
|
|
| 8 |
</head>
|
| 9 |
<body>
|
| 10 |
<div id="root"></div>
|
| 11 |
+
<script type="module" src="./app.js"></script>
|
| 12 |
</body>
|
| 13 |
</html>
|
extension/sidebar/styles/sidebar.css
CHANGED
|
@@ -82,12 +82,28 @@ body {
|
|
| 82 |
min-height: 128px;
|
| 83 |
}
|
| 84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
.workflow-grid,
|
| 86 |
.action-row {
|
| 87 |
display: grid;
|
| 88 |
gap: 12px;
|
| 89 |
}
|
| 90 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
.dashboard-row,
|
| 92 |
.status-row {
|
| 93 |
display: flex;
|
|
@@ -127,6 +143,14 @@ body {
|
|
| 127 |
width: 100%;
|
| 128 |
}
|
| 129 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
.status-badge {
|
| 131 |
display: inline-flex;
|
| 132 |
align-items: center;
|
|
|
|
| 82 |
min-height: 128px;
|
| 83 |
}
|
| 84 |
|
| 85 |
+
.field-label {
|
| 86 |
+
display: block;
|
| 87 |
+
margin: 14px 0 8px;
|
| 88 |
+
font-size: 12px;
|
| 89 |
+
font-weight: 600;
|
| 90 |
+
color: var(--muted);
|
| 91 |
+
text-transform: uppercase;
|
| 92 |
+
letter-spacing: 0.06em;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
.workflow-grid,
|
| 96 |
.action-row {
|
| 97 |
display: grid;
|
| 98 |
gap: 12px;
|
| 99 |
}
|
| 100 |
|
| 101 |
+
.button-row {
|
| 102 |
+
display: flex;
|
| 103 |
+
gap: 10px;
|
| 104 |
+
margin-top: 12px;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
.dashboard-row,
|
| 108 |
.status-row {
|
| 109 |
display: flex;
|
|
|
|
| 143 |
width: 100%;
|
| 144 |
}
|
| 145 |
|
| 146 |
+
.status-copy {
|
| 147 |
+
margin: 12px 0 0;
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
.error-copy {
|
| 151 |
+
color: #9e2f1f;
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
.status-badge {
|
| 155 |
display: inline-flex;
|
| 156 |
align-items: center;
|