Skip to content

Commit ff47f57

Browse files
author
A.R.
committed
docs: root README rewrite + hero SVG + badge suite + IDE matrix + browser table
Complete rewrite of root README.md for GitHub + npm dual-audience. Add SVG hero with dark/light theme support, badge row (Marketplace, npm version/downloads, CI, license, status), and three reference tables: "Who should use what?" (extension vs npm vs daemon), browser support matrix (Chrome/Edge/Chromium/Brave/bundled with env hints), and IDE/MCP client wiring (15+ targets with config artifacts). Expand Quick Start with
1 parent d0e58e9 commit ff47f57

8 files changed

Lines changed: 319 additions & 60 deletions

File tree

README.md

Lines changed: 285 additions & 41 deletions
Large diffs are not rendered by default.

packages/extension/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "perplexity-vscode",
33
"displayName": "Perplexity MCP",
4-
"version": "0.8.32",
4+
"version": "0.8.34",
55
"publisher": "Automations-Project",
66
"private": true,
77
"description": "Native VS Code extension for the Perplexity MCP server with dashboard, auth, and multi-IDE config generation",

packages/mcp-server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "perplexity-user-mcp",
3-
"version": "0.8.32",
3+
"version": "0.8.34",
44
"type": "module",
55
"description": "Perplexity AI MCP server — browser automation for search, reasoning, research, and compute",
66
"author": "A.R.",

packages/shared/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@perplexity-user-mcp/shared",
3-
"version": "0.1.15",
3+
"version": "0.1.17",
44
"type": "module",
55
"main": "src/index.ts",
66
"types": "src/index.ts",

packages/webview/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@perplexity-user-mcp/webview",
3-
"version": "0.1.18",
3+
"version": "0.1.20",
44
"private": true,
55
"type": "module",
66
"scripts": {

packages/webview/src/components/DownloadMenu.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
2+
import { createPortal } from "react-dom";
23
import {
34
ChevronDown,
45
Download,
@@ -31,6 +32,7 @@ export function DownloadMenu({ item, send }: { item: HistoryItem; send: SendFn }
3132
const [menuPosition, setMenuPosition] = useState<React.CSSProperties>();
3233
const containerRef = useRef<HTMLDivElement>(null);
3334
const toggleRef = useRef<HTMLButtonElement>(null);
35+
const popoverRef = useRef<HTMLDivElement>(null);
3436

3537
const hasMarkdown = item.answerPreview.trim().length > 0;
3638
const toggleDisabled = !hasMarkdown && !item.threadSlug;
@@ -76,7 +78,7 @@ export function DownloadMenu({ item, send }: { item: HistoryItem; send: SendFn }
7678
};
7779
}, [open, updateMenuPosition]);
7880

79-
useDisclosureMenu({ triggerRef: toggleRef, menuRef: containerRef, isOpen: open, onClose: close });
81+
useDisclosureMenu({ triggerRef: toggleRef, menuRef: containerRef, popoverRef, isOpen: open, onClose: close });
8082

8183
const onToggleKey = (e: React.KeyboardEvent) => {
8284
if (e.key === "Enter" || e.key === " ") {
@@ -111,8 +113,8 @@ export function DownloadMenu({ item, send }: { item: HistoryItem; send: SendFn }
111113
Download
112114
<ChevronDown size={11} className="hist-action-caret" aria-hidden="true" />
113115
</button>
114-
{open && (
115-
<div className="hist-menu-popover hist-menu-popover-fixed" role="menu" style={menuPosition}>
116+
{open && createPortal(
117+
<div ref={popoverRef} className="hist-menu-popover hist-menu-popover-fixed" role="menu" style={menuPosition}>
116118
<div className="hist-menu-section-label">Export as</div>
117119
{FORMATS.map((format) => {
118120
const { disabled, reason } = formatIsDisabled(format);
@@ -141,7 +143,8 @@ export function DownloadMenu({ item, send }: { item: HistoryItem; send: SendFn }
141143
<kbd className="hist-menu-footer-kbd">Esc</kbd>
142144
<span>close</span>
143145
</div>
144-
</div>
146+
</div>,
147+
document.body
145148
)}
146149
</div>
147150
);

packages/webview/src/components/OpenWithMenu.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
2+
import { createPortal } from "react-dom";
23
import {
34
BookOpen,
45
ChevronDown,
@@ -25,6 +26,7 @@ export function OpenWithMenu({
2526
const [menuPosition, setMenuPosition] = useState<React.CSSProperties>();
2627
const containerRef = useRef<HTMLDivElement>(null);
2728
const toggleRef = useRef<HTMLButtonElement>(null);
29+
const popoverRef = useRef<HTMLDivElement>(null);
2830

2931
const close = useCallback(() => setOpen(false), []);
3032
const updateMenuPosition = useCallback(() => {
@@ -68,7 +70,7 @@ export function OpenWithMenu({
6870
};
6971
}, [open, updateMenuPosition]);
7072

71-
useDisclosureMenu({ triggerRef: toggleRef, menuRef: containerRef, isOpen: open, onClose: close });
73+
useDisclosureMenu({ triggerRef: toggleRef, menuRef: containerRef, popoverRef, isOpen: open, onClose: close });
7274

7375
const onToggleKey = (e: React.KeyboardEvent) => {
7476
if (e.key === "Enter" || e.key === " ") {
@@ -95,8 +97,8 @@ export function OpenWithMenu({
9597
Open with
9698
<ChevronDown size={11} className="hist-action-caret" aria-hidden="true" />
9799
</button>
98-
{open && (
99-
<div className="hist-menu-popover hist-menu-popover-fixed" role="menu" style={menuPosition}>
100+
{open && createPortal(
101+
<div ref={popoverRef} className="hist-menu-popover hist-menu-popover-fixed" role="menu" style={menuPosition}>
100102
<div className="hist-menu-section-label">Built-in</div>
101103
<button
102104
className="hist-menu-item"
@@ -166,7 +168,8 @@ export function OpenWithMenu({
166168
<kbd className="hist-menu-footer-kbd">Esc</kbd>
167169
<span>close</span>
168170
</div>
169-
</div>
171+
</div>,
172+
document.body
170173
)}
171174
</div>
172175
);

packages/webview/src/lib/useDisclosureMenu.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ export interface DisclosureMenuOptions {
77
triggerRef: React.RefObject<HTMLElement | null>;
88
/** Ref to the container that wraps both the trigger and the menu popover. */
99
menuRef: React.RefObject<HTMLElement | null>;
10+
/**
11+
* Optional ref to a portaled popover element rendered outside `menuRef`.
12+
* When provided, item querying and click-outside detection also consider
13+
* this element so that React-portaled menus work correctly.
14+
*/
15+
popoverRef?: React.RefObject<HTMLElement | null>;
1016
isOpen: boolean;
1117
onClose: () => void;
1218
/**
@@ -31,7 +37,7 @@ export interface DisclosureMenuOptions {
3137
* cleanup / close.
3238
*/
3339
export function useDisclosureMenu(opts: DisclosureMenuOptions): void {
34-
const { triggerRef, menuRef, isOpen, onClose, itemSelector = DEFAULT_ITEM_SELECTOR } = opts;
40+
const { triggerRef, menuRef, popoverRef, isOpen, onClose, itemSelector = DEFAULT_ITEM_SELECTOR } = opts;
3541

3642
// Keep a stable ref so callbacks inside the effect always see the latest onClose
3743
// without having to re-subscribe every render.
@@ -43,13 +49,14 @@ export function useDisclosureMenu(opts: DisclosureMenuOptions): void {
4349
if (!isOpen) return;
4450
// Defer one frame so the menu DOM is guaranteed to be in the tree.
4551
const frame = requestAnimationFrame(() => {
46-
const items = menuRef.current?.querySelectorAll<HTMLElement>(
52+
const root = popoverRef?.current ?? menuRef.current;
53+
const items = root?.querySelectorAll<HTMLElement>(
4754
`${itemSelector}:not([disabled]):not(:disabled)`
4855
);
4956
items?.[0]?.focus();
5057
});
5158
return () => cancelAnimationFrame(frame);
52-
}, [isOpen, menuRef, itemSelector]);
59+
}, [isOpen, menuRef, popoverRef, itemSelector]);
5360

5461
// Keyboard and click-outside handlers.
5562
useEffect(() => {
@@ -66,8 +73,9 @@ export function useDisclosureMenu(opts: DisclosureMenuOptions): void {
6673

6774
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
6875
e.preventDefault();
76+
const root = popoverRef?.current ?? menuRef.current;
6977
const items = Array.from(
70-
menuRef.current?.querySelectorAll<HTMLElement>(
78+
root?.querySelectorAll<HTMLElement>(
7179
`${itemSelector}:not([disabled]):not(:disabled)`
7280
) ?? []
7381
);
@@ -84,15 +92,16 @@ export function useDisclosureMenu(opts: DisclosureMenuOptions): void {
8492

8593
if (e.key === "Enter") {
8694
const active = document.activeElement as HTMLElement | null;
87-
if (active && menuRef.current?.contains(active)) {
95+
if (active && (menuRef.current?.contains(active) || popoverRef?.current?.contains(active))) {
8896
e.preventDefault();
8997
active.click();
9098
}
9199
}
92100
};
93101

94102
const onClick = (e: MouseEvent) => {
95-
if (!menuRef.current?.contains(e.target as Node)) {
103+
const target = e.target as Node;
104+
if (!menuRef.current?.contains(target) && !popoverRef?.current?.contains(target)) {
96105
onCloseRef.current();
97106
// No focus restoration on outside-click — the user clicked somewhere
98107
// intentionally and that element may already hold focus.
@@ -105,5 +114,5 @@ export function useDisclosureMenu(opts: DisclosureMenuOptions): void {
105114
document.removeEventListener("keydown", onKey);
106115
document.removeEventListener("click", onClick);
107116
};
108-
}, [isOpen, triggerRef, menuRef, itemSelector]);
117+
}, [isOpen, triggerRef, menuRef, popoverRef, itemSelector]);
109118
}

0 commit comments

Comments
 (0)