seemore 1.1.2 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seemore",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "Let AI write the Markdown. Let seemore show it better — zero config documentation framework.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -4,12 +4,9 @@ import { RouterProvider, createBrowserRouter } from 'react-router';
4
4
  import { config } from 'virtual:seemore/config';
5
5
  import { decodePath, stripBase, toBasename } from '../shared/base.js';
6
6
  import { createRouteObjects } from './router.js';
7
- import { installCopyBridge } from './lib/copyBridge.js';
8
7
  import { preloadPage } from './lib/pages.js';
9
8
  import './styles/globals.css';
10
9
 
11
- installCopyBridge();
12
-
13
10
  const container = document.getElementById('root');
14
11
  if (container === null) throw new Error('seemore: #root is missing from the page shell.');
15
12
 
@@ -19,6 +19,7 @@ import { Header } from './Header.js';
19
19
  import { Sidebar } from './Sidebar.js';
20
20
  import { Breadcrumb } from './Breadcrumb.js';
21
21
  import { BackToTop, PageFooter, SiteFooter } from './Footer.js';
22
+ import { SelectionCopyButton } from './SelectionCopyButton.js';
22
23
  import { IntegratedToc, Toc, TocProvider } from './Toc.js';
23
24
 
24
25
  /**
@@ -82,6 +83,7 @@ export function DocPage({ entry }: { entry: RouteEntry }) {
82
83
 
83
84
  {feature('navigation.top') ? <BackToTop /> : undefined}
84
85
  <PagePreview />
86
+ <SelectionCopyButton />
85
87
  </div>
86
88
  </TocProvider>
87
89
  );
@@ -0,0 +1,56 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ /**
4
+ * A floating "Copy" button that appears over the current text selection.
5
+ *
6
+ * Only matters embedded in the VS Code extension's webview: the page runs there inside a
7
+ * nested, cross-origin iframe, and the workbench's own Ctrl+C/Cmd+C handling never reaches a
8
+ * selection made inside it. A click does — but the write itself still can't happen here:
9
+ * VS Code webviews deny the Clipboard API to embedded content regardless of how it's
10
+ * triggered, so the click instead posts the selected text up to the extension host (see
11
+ * `panelHtml.ts`/`panel.ts`), which is the only side actually allowed to touch the OS
12
+ * clipboard (`vscode.env.clipboard`). A plain browser tab already has native copy and is
13
+ * never embedded this way, so this renders nothing there.
14
+ */
15
+ export function SelectionCopyButton() {
16
+ const [rect, setRect] = useState<DOMRect>();
17
+ const [copied, setCopied] = useState(false);
18
+
19
+ useEffect(() => {
20
+ if (window.parent === window) return;
21
+
22
+ function onSelectionChange() {
23
+ const selection = window.getSelection();
24
+ if (selection === null || selection.isCollapsed || selection.toString().trim() === '') {
25
+ setRect(undefined);
26
+ return;
27
+ }
28
+ setRect(selection.getRangeAt(0).getBoundingClientRect());
29
+ setCopied(false);
30
+ }
31
+
32
+ document.addEventListener('selectionchange', onSelectionChange);
33
+ return () => document.removeEventListener('selectionchange', onSelectionChange);
34
+ }, []);
35
+
36
+ if (rect === undefined) return undefined;
37
+
38
+ return (
39
+ <button
40
+ type="button"
41
+ className="seemore-selection-copy"
42
+ style={{ top: Math.max(rect.top - 36, 8), left: rect.left }}
43
+ // A button's default mousedown behaviour collapses whatever is currently selected
44
+ // before the click ever fires — this is what keeps the selection alive through it.
45
+ onMouseDown={(event) => event.preventDefault()}
46
+ onClick={() => {
47
+ const text = window.getSelection()?.toString() ?? '';
48
+ if (text === '') return;
49
+ window.parent.postMessage({ type: 'seemore:copy', text }, '*');
50
+ setCopied(true);
51
+ }}
52
+ >
53
+ {copied ? 'Copied' : 'Copy'}
54
+ </button>
55
+ );
56
+ }
@@ -244,6 +244,10 @@
244
244
  @apply fixed bottom-6 end-6 inline-flex items-center gap-2 rounded-full border border-fd-border bg-fd-background px-4 py-2 text-sm shadow;
245
245
  }
246
246
 
247
+ .seemore-selection-copy {
248
+ @apply fixed z-50 rounded-md border border-fd-border bg-fd-popover px-2 py-1 text-xs font-medium shadow-lg;
249
+ }
250
+
247
251
  .seemore-preview {
248
252
  @apply pointer-events-none fixed z-50 max-h-80 w-[400px] overflow-hidden rounded-xl border border-fd-border bg-fd-popover p-4 text-sm shadow-lg;
249
253
  }
@@ -1,28 +0,0 @@
1
- /**
2
- * Answers a clipboard-copy request from an embedding parent frame (the seemore VS Code
3
- * extension's webview shell — see `panelHtml.ts`/`panel.ts` in that package).
4
- *
5
- * Embedded there, this page runs inside a nested, cross-origin iframe, and the host's own
6
- * Ctrl+C/Cmd+C keybinding never reaches this document's native selection-copy at all — the
7
- * extension asks for the current selection over `postMessage` instead and writes it to the
8
- * clipboard itself. Outside that embedding, nothing ever posts this message, so the listener
9
- * is otherwise inert.
10
- */
11
- interface CopyRequest {
12
- type: 'seemore:copy-request';
13
- requestId: string;
14
- }
15
-
16
- function isCopyRequest(value: unknown): value is CopyRequest {
17
- if (typeof value !== 'object' || value === null) return false;
18
- const candidate = value as Record<string, unknown>;
19
- return candidate.type === 'seemore:copy-request' && typeof candidate.requestId === 'string';
20
- }
21
-
22
- export function installCopyBridge(): void {
23
- window.addEventListener('message', (event) => {
24
- if (event.source !== window.parent || !isCopyRequest(event.data)) return;
25
- const text = window.getSelection()?.toString() ?? '';
26
- window.parent.postMessage({ type: 'seemore:copy-response', requestId: event.data.requestId, text }, '*');
27
- });
28
- }