Skip to content

Commit d194518

Browse files
committed
more ui fixes
1 parent 155651c commit d194518

4 files changed

Lines changed: 70 additions & 13 deletions

File tree

apps/extension/src/App.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ export interface AppProps {
1919
onPreviewSlot?: (event: EventItem | null) => void;
2020
}
2121

22-
const DASHBOARD_URL =
23-
(import.meta.env.VITE_DASHBOARD_URL as string | undefined) ??
24-
"https://cornellloop.com";
22+
const DASHBOARD_URL = (() => {
23+
const value = import.meta.env.VITE_DASHBOARD_URL;
24+
return typeof value === "string" && value.length > 0
25+
? value
26+
: "https://cornellloop.com";
27+
})();
2528

2629
export default function App({
2730
onClose,
@@ -59,9 +62,9 @@ export default function App({
5962
const isSearchMode = view === "search" || view === "email";
6063

6164
const handleTabChange = (tab: string) => {
62-
const t = tab as "feed" | "bookmarks";
63-
setActiveTab(t);
64-
setView(t);
65+
if (tab !== "feed" && tab !== "bookmarks") return;
66+
setActiveTab(tab);
67+
setView(tab);
6568
setEmailEvent(null);
6669
};
6770

@@ -92,7 +95,7 @@ export default function App({
9295
style={{ boxShadow: "0px 4px 16px rgba(0, 0, 0, 0.18)" }}
9396
>
9497
{/* ── Sticky header ── */}
95-
<div className="shrink-0 px-6 pt-7">
98+
<div className="shrink-0 px-6 pt-7" data-loop-panel-drag>
9699
<SearchHeader
97100
variant={isSearchMode ? "search" : "main"}
98101
activeTab={activeTab}
@@ -109,7 +112,10 @@ export default function App({
109112
{/* ── Main content: search uses pinned footer CTA; other views scroll with CTA inline ── */}
110113
{view === "search" ? (
111114
<div className="flex min-h-0 flex-1 flex-col">
112-
<div className="min-h-0 flex-1 overflow-y-auto px-6 py-[21px]">
115+
<div
116+
className="min-h-0 flex-1 overflow-y-auto px-6 py-[21px]"
117+
data-loop-scroll
118+
>
113119
<SearchView
114120
query={searchQuery}
115121
onSearchSelect={handleSearchSelect}
@@ -137,7 +143,10 @@ export default function App({
137143
</div>
138144
</div>
139145
) : (
140-
<div className="min-h-0 flex-1 overflow-y-auto px-6 py-[21px]">
146+
<div
147+
className="min-h-0 flex-1 overflow-y-auto px-6 py-[21px]"
148+
data-loop-scroll
149+
>
141150
{view === "feed" && (
142151
<FeedView
143152
bookmarkedIds={bookmarkedIds}

apps/extension/src/background.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,10 @@
1-
// No side panel logic needed — UI is injected via content script.
2-
export {};
1+
// When the user clicks the toolbar icon, tell the active tab's content script
2+
// to re-show the floating panel (in case it was previously dismissed).
3+
chrome.action.onClicked.addListener((tab) => {
4+
if (tab.id === undefined) return;
5+
void chrome.tabs
6+
.sendMessage(tab.id, { type: "LOOP_SHOW_PANEL" })
7+
.catch(() => {
8+
// Content script not injected on this page — silently ignore.
9+
});
10+
});

apps/extension/src/content.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ import { ConvexReactClient } from "convex/react";
55
import FloatingPanel from "./components/FloatingPanel.tsx";
66
import contentStyles from "./content.css?inline";
77
import { showSlotPreview, removeSlotPreview } from "./gcalHighlight";
8+
import { panelEvents } from "./panelBridge";
89
import type { EventItem } from "./data/types";
910
import type { PageContext } from "./App";
1011

11-
const convexUrl = import.meta.env.VITE_CONVEX_URL as string | undefined;
12+
const convexUrl = (() => {
13+
const value = import.meta.env.VITE_CONVEX_URL;
14+
return typeof value === "string" && value.length > 0 ? value : undefined;
15+
})();
1216

1317
function loadFonts() {
1418
if (document.getElementById("cornell-loop-fonts")) return;
@@ -90,3 +94,20 @@ function mount() {
9094
}
9195

9296
mount();
97+
98+
// Re-show the floating panel when the user clicks the toolbar icon
99+
// (handled in background.ts via chrome.action.onClicked).
100+
function readMessageType(raw: unknown): string | null {
101+
if (typeof raw !== "object" || raw === null) return null;
102+
for (const [k, v] of Object.entries(raw)) {
103+
if (k === "type" && typeof v === "string") return v;
104+
}
105+
return null;
106+
}
107+
108+
chrome.runtime.onMessage.addListener((rawMessage) => {
109+
if (readMessageType(rawMessage) === "LOOP_SHOW_PANEL") {
110+
panelEvents.dispatchEvent(new Event("show"));
111+
}
112+
return false;
113+
});

apps/extension/vite.config.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,31 @@ import { fileURLToPath } from "url";
88

99
const __dirname = path.dirname(fileURLToPath(import.meta.url));
1010

11+
// PLAYWRIGHT is set by the package script (`PLAYWRIGHT=true vite build`), not .env.
12+
function isPlaywrightBuild(): boolean {
13+
return process.env["PLAYWRIGHT"] === "true";
14+
}
15+
1116
export default defineConfig({
1217
plugins: [
1318
react(),
1419
tailwindcss(),
1520
svgr(),
16-
webExtension({ manifest: "./manifest.json" }),
21+
webExtension({
22+
manifest: "./manifest.json",
23+
transformManifest(manifest) {
24+
if (!isPlaywrightBuild()) return manifest;
25+
const cs = manifest.content_scripts;
26+
if (!Array.isArray(cs)) return manifest;
27+
for (const entry of cs) {
28+
if (!Array.isArray(entry.matches)) continue;
29+
if (!entry.matches.includes("http://localhost/*")) {
30+
entry.matches.push("http://localhost/*");
31+
}
32+
}
33+
return manifest;
34+
},
35+
}),
1736
],
1837
build: {
1938
cssCodeSplit: false,

0 commit comments

Comments
 (0)