mirror of
https://github.com/jldiaz/copy-protocol-plugin.git
synced 2026-07-22 06:56:55 +00:00
Release 1.1.2: Fixed Reading Mode icons and resolved strict validator warnings
This commit is contained in:
parent
f31f9ed5eb
commit
4983397c7f
6 changed files with 87 additions and 59 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "copy-text-protocol",
|
||||
"name": "Copy Text Protocol",
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.2",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "Adds support for copying text to the clipboard using a custom protocol.",
|
||||
"author": "jldiaz",
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "copy-text-protocol",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "copy-text-protocol",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"license": "0-BSD",
|
||||
"dependencies": {
|
||||
"obsidian": "latest"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "copy-text-protocol",
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.2",
|
||||
"description": "A plugin that adds support for copying text via a custom protocol.",
|
||||
"main": "main.js",
|
||||
"type": "module",
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
"typescript-eslint": "8.35.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.2.3",
|
||||
"obsidian": "latest"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
91
src/main.ts
91
src/main.ts
|
|
@ -58,54 +58,59 @@ const copyLinkPlugin = ViewPlugin.fromClass(
|
|||
const { from, to } = view.viewport;
|
||||
const selection = view.state.selection;
|
||||
|
||||
tree.iterate({
|
||||
from,
|
||||
to,
|
||||
enter(node: SyntaxNodeRef) {
|
||||
if (!node.name.includes('string_url')) return;
|
||||
const urlText = view.state.doc.sliceString(node.from, node.to);
|
||||
if (!urlText.includes('obsidian://copy')) return;
|
||||
for (const { from: lFrom, to: lTo } of view.visibleRanges) {
|
||||
tree.iterate({
|
||||
from: lFrom,
|
||||
to: lTo,
|
||||
enter(node: SyntaxNodeRef) {
|
||||
if (!node.name.includes('string_url')) return;
|
||||
const urlText = view.state.doc.sliceString(node.from, node.to);
|
||||
if (!urlText.includes('obsidian://copy')) return;
|
||||
|
||||
let textToCopy = '';
|
||||
try {
|
||||
const cleanUrl = urlText.startsWith('<') ? urlText.slice(1, -1) : urlText;
|
||||
const url = new URL(cleanUrl);
|
||||
textToCopy = url.searchParams.get('text') ?? '';
|
||||
} catch {
|
||||
const match = urlText.match(/[?&]text=([^&> ]+)/);
|
||||
textToCopy = (match && match[1]) ? decodeURIComponent(match[1]) : '';
|
||||
}
|
||||
|
||||
if (!textToCopy) return;
|
||||
|
||||
let labelNode: SyntaxNodeRef | null = null;
|
||||
let curr = node.node.prevSibling;
|
||||
for (let i = 0; i < 6 && curr; i++) {
|
||||
if (curr.name.includes('link') && !curr.name.includes('formatting')) {
|
||||
labelNode = curr as unknown as SyntaxNodeRef;
|
||||
break;
|
||||
let textToCopy = '';
|
||||
try {
|
||||
const cleanUrl = urlText.startsWith('<') ? urlText.slice(1, -1) : urlText;
|
||||
const url = new URL(cleanUrl);
|
||||
textToCopy = url.searchParams.get('text') ?? '';
|
||||
} catch {
|
||||
const match = urlText.match(/[?&]text=([^&> ]+)/);
|
||||
textToCopy = (match && match[1]) ? decodeURIComponent(match[1]) : '';
|
||||
}
|
||||
curr = curr.prevSibling;
|
||||
}
|
||||
|
||||
if (labelNode) {
|
||||
// Hide widget if cursor is anywhere near the link structure
|
||||
// (from the start of label to the end of the URL)
|
||||
const linkStart = labelNode.from - 1; // accounting for '['
|
||||
const linkEnd = node.to + 1; // accounting for ')'
|
||||
const isEditing = selection.ranges.some(r => r.from <= linkEnd && r.to >= linkStart);
|
||||
if (!textToCopy) return;
|
||||
|
||||
builder.add(labelNode.from, labelNode.to, Decoration.mark({ class: 'copy-protocol-link' }));
|
||||
|
||||
if (!isEditing) {
|
||||
builder.add(labelNode.to, labelNode.to, Decoration.widget({
|
||||
widget: new CopyIconWidget(textToCopy),
|
||||
side: 1
|
||||
}));
|
||||
let labelNode: SyntaxNodeRef | null = null;
|
||||
let curr = node.node.prevSibling;
|
||||
for (let i = 0; i < 6 && curr; i++) {
|
||||
if (curr.name.includes('link') && !curr.name.includes('formatting')) {
|
||||
labelNode = curr as unknown as SyntaxNodeRef;
|
||||
break;
|
||||
}
|
||||
curr = curr.prevSibling;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (labelNode) {
|
||||
// Mark the line to hide external link icons via CSS
|
||||
const line = view.state.doc.lineAt(node.from);
|
||||
builder.add(line.from, line.from, Decoration.line({ class: 'has-copy-protocol-line' }));
|
||||
|
||||
// Hide widget if cursor is anywhere near the link structure
|
||||
const linkStart = labelNode.from - 1;
|
||||
const linkEnd = node.to + 1;
|
||||
const isEditing = selection.ranges.some(r => r.from <= linkEnd && r.to >= linkStart);
|
||||
|
||||
builder.add(labelNode.from, labelNode.to, Decoration.mark({ class: 'copy-protocol-link' }));
|
||||
|
||||
if (!isEditing) {
|
||||
builder.add(labelNode.to, labelNode.to, Decoration.widget({
|
||||
widget: new CopyIconWidget(textToCopy),
|
||||
side: 1
|
||||
}));
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
return builder.finish();
|
||||
}
|
||||
},
|
||||
|
|
|
|||
45
styles.css
45
styles.css
|
|
@ -4,7 +4,18 @@
|
|||
|
||||
/* ── Reading View (Standard Links) ── */
|
||||
|
||||
a[href^="obsidian://copy"]::after {
|
||||
/*
|
||||
* We target the same pseudo-element Obsidian uses (::after) but with higher
|
||||
* specificity to override it completely without !important.
|
||||
*/
|
||||
.markdown-rendered a.external-link[href^="obsidian://copy"],
|
||||
.markdown-preview-view a.external-link[href^="obsidian://copy"] {
|
||||
background-image: none;
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
|
||||
.markdown-rendered a.external-link[href^="obsidian://copy"]::after,
|
||||
.markdown-preview-view a.external-link[href^="obsidian://copy"]::after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 0.85em;
|
||||
|
|
@ -12,6 +23,7 @@ a[href^="obsidian://copy"]::after {
|
|||
margin-left: 5px;
|
||||
vertical-align: middle;
|
||||
background-color: currentColor;
|
||||
background-image: none; /* Clears Obsidian's default icon if set via background-image */
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect width='14' height='14' x='8' y='8' rx='2' ry='2'/%3E%3Cpath d='M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2'/%3E%3C/svg%3E");
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect width='14' height='14' x='8' y='8' rx='2' ry='2'/%3E%3Cpath d='M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2'/%3E%3C/svg%3E");
|
||||
-webkit-mask-size: contain;
|
||||
|
|
@ -21,34 +33,43 @@ a[href^="obsidian://copy"]::after {
|
|||
opacity: 0.8;
|
||||
}
|
||||
|
||||
a[href^="obsidian://copy"]:hover::after {
|
||||
.markdown-rendered a.external-link[href^="obsidian://copy"]:hover::after,
|
||||
.markdown-preview-view a.external-link[href^="obsidian://copy"]:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Hide Obsidian's default external link icon elements in Reading Mode */
|
||||
.markdown-rendered a[href^="obsidian://copy"] .external-link,
|
||||
.markdown-rendered a[href^="obsidian://copy"] + .external-link,
|
||||
.markdown-preview-view a[href^="obsidian://copy"] .external-link,
|
||||
.markdown-preview-view a[href^="obsidian://copy"] + .external-link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ── Live Preview (Managed by ViewPlugin Widget) ── */
|
||||
|
||||
/*
|
||||
* Hide Obsidian's default external link icon on the same line as our copy links.
|
||||
* We still use the .copy-protocol-link mark for this.
|
||||
* Hide Obsidian's default external link icon on our copy links.
|
||||
* Instead of :has(), we use the .has-copy-protocol-line class added in JS.
|
||||
*/
|
||||
.cm-line:has(.copy-protocol-link) .external-link,
|
||||
.cm-line:has(.copy-protocol-link) .cm-url.external-link,
|
||||
.cm-line:has(.copy-protocol-link) .cm-widgetBuffer + .external-link {
|
||||
display: none !important;
|
||||
.markdown-source-view.mod-cm6 .has-copy-protocol-line .external-link,
|
||||
.markdown-source-view.mod-cm6 .has-copy-protocol-line .cm-url.external-link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Ensure the link text looks like a link */
|
||||
.copy-protocol-link {
|
||||
.markdown-source-view.mod-cm6 .copy-protocol-link {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Copy cursor on hover */
|
||||
.copy-protocol-link:hover,
|
||||
a[href^="obsidian://copy"]:hover {
|
||||
cursor: copy !important;
|
||||
.markdown-source-view.mod-cm6 .copy-protocol-link:hover,
|
||||
.markdown-source-view.mod-cm6 a[href^="obsidian://copy"]:hover {
|
||||
cursor: copy;
|
||||
}
|
||||
|
||||
|
||||
/* ── Widget Styles (Live Preview) ── */
|
||||
|
||||
.copy-protocol-icon {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"1.1.2": "0.15.0",
|
||||
"1.1.1": "0.15.0",
|
||||
"1.1.0": "0.15.0",
|
||||
"1.0.1": "0.15.0",
|
||||
|
|
|
|||
Loading…
Reference in a new issue