Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/assets/icons/markdown.svg

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ellen provided an icon in Slack that looked quite different to this one. I see in the PR description it's mentioned that it should be switched.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh didn't notice that, ill update it to use the one she provided, thanks for spotting that!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rosslovas Done thanks!

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
109 changes: 91 additions & 18 deletions src/components/CopyAsMarkdown.astro
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
import { Lang, Translations } from '@util/Languages';
import { getPageMarkdownPath } from '@util/mdxContent';
import Button from './Button.astro';
import SplitButton from './SplitButton.astro';

type Props = {
lang: string;
Expand All @@ -15,31 +15,104 @@ const pageMdUrl = getPageMarkdownPath(Astro.url.pathname);

{
pageMdUrl && (
<Button
<SplitButton
class="octo-copy-md"
data-copy-md-url={pageMdUrl}
label={_(Translations.octopus_copy_md.label)}
icon="octo-copy-md__icon"
size="small"
data-copy-md-url={pageMdUrl}
menuLabel={_(Translations.octopus_copy_md.more_options)}
items={[
{
label: _(Translations.octopus_copy_md.view_raw),
href: pageMdUrl,
icon: 'octo-copy-md__glyph octo-copy-md__glyph--markdown',
iconEnd: 'octo-copy-md__glyph octo-copy-md__glyph--external',
},
]}
target="_blank"
rel="noopener"
>
{/* All three states are rendered, stacked in one grid cell, so the button
is sized by its longest label and keeps that width as the state
changes. Only the current one is visible, and `visibility` keeps the
other two out of the accessible name. */}
<span class="btn__label octo-copy-md__labels">
<span class="octo-copy-md__label octo-copy-md__label--rest">
{_(Translations.octopus_copy_md.label)}
</span>
<span class="octo-copy-md__label octo-copy-md__label--copied">
{_(Translations.octopus_copy_md.copied)}
</span>
<span class="octo-copy-md__label octo-copy-md__label--failed">
{_(Translations.octopus_copy_md.error)}
</span>
{/* Stacked with the label in one grid cell, so the button is sized by the
longer of the two and keeps that width while it reports the copy.
`visibility` is what hides it, which also keeps it out of the
accessible name until it is the one showing. */}
<span class="octo-copy-md__copied">
{_(Translations.octopus_copy_md.copied)}
</span>
</Button>
</SplitButton>
)
}

<style>
/* SplitButton, Button and Menu render everything inside this control, so those
elements carry their own scope rather than this one and are reached with
`:global()`. The `.octo-copy-md` the wrapper is given does carry it, which is
what keeps these rules off every other button on the page. */

.octo-copy-md :global(.octo-copy-md__icon) {
background-color: var(--colorIconPrimary);
mask: url('../assets/icons/copy.svg') center / contain no-repeat;
}

.octo-copy-md :global([data-copied] .octo-copy-md__icon) {
mask: url('../assets/icons/check.svg') center / contain no-repeat;
}

/* The label and the "Copied" it turns into share one grid cell, so the button
is sized by the longer of the two and holds that width while it reports. The
icon keeps a column of its own, as `.btn`'s flex layout gave it. */
.octo-copy-md :global(.split-btn__primary) {
display: inline-grid;
grid-template-columns: auto auto;
align-items: center;
justify-content: center;
}

.octo-copy-md :global(.split-btn__primary > .btn__icon) {
grid-area: 1 / 1;
}

.octo-copy-md :global(.split-btn__primary > .btn__label),
.octo-copy-md__copied {
grid-area: 1 / 2;
text-align: start;
white-space: nowrap;
}

/* Slotted, so button.css's scoped .btn__label padding does not reach it. */
.octo-copy-md__copied {
padding-inline: var(--space4);
visibility: hidden;
}

.octo-copy-md :global([data-copied] > .btn__label) {
visibility: hidden;
}

.octo-copy-md :global([data-copied]) > .octo-copy-md__copied {
visibility: visible;
}

/* The menu item's own glyphs. */
.octo-copy-md :global(.octo-copy-md__glyph) {
background-color: currentColor;
}

/* Drawn at the 16px it was exported at, which is the box `.menu__icon` gives
it, so `contain` fits it rather than scaling it. The badge is short of the
full height because the asset carries its own vertical padding. */
.octo-copy-md :global(.octo-copy-md__glyph--markdown) {
mask: url('../assets/icons/markdown.svg') center / contain no-repeat;
}

/* The exported glyph sits inside a box 25% wider than itself, so the mask is
sized rather than left to fill. */
.octo-copy-md :global(.octo-copy-md__glyph--external) {
mask: url('../assets/icons/external-link.svg') center / 0.8rem no-repeat;
}
</style>

<script>
import '../scripts/copy-as-markdown';
</script>
57 changes: 0 additions & 57 deletions src/components/MarkdownLinks.astro

This file was deleted.

17 changes: 12 additions & 5 deletions src/components/SplitButton.astro
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export type SplitButtonItem = {

type Props = HTMLAttributes<'div'> & {
label: string;
href: string | URL;
// Omitted when the primary acts on the page rather than navigating away
href?: string | URL;
icon?: string;
size?: 'xSmall' | 'small' | 'medium';
importance?: 'default' | 'loud';
Expand Down Expand Up @@ -40,6 +41,12 @@ if (items.length === 0) {
throw new Error('A SplitButton with no menu items should just be a Button');
}

// Button types the two halves of its own union separately - an anchor needs an
// `href`, a plain button forbids one - so the link attributes are handed over as
// a set rather than as three possibly-undefined props. `target` and `rel` say
// where a link opens, and mean nothing on a button.
const primaryLink = href ? { href, target, rel } : {};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allows "non href" button that doesn't link anywhere. (client side only)


const menuItems = items.map((item) => ({
...item,
kind: 'link' as const,
Expand All @@ -51,14 +58,14 @@ const menuItems = items.map((item) => ({
<div class:list={['split-btn', className]} {...rest}>
<Button
class="split-btn__primary"
href={href}
{...primaryLink}
icon={icon}
label={label}
size={size}
importance={importance}
target={target}
rel={rel}
/>
>
<slot />
</Button>
<Menu
class="split-btn__menu"
label={menuLabel}
Expand Down
18 changes: 5 additions & 13 deletions src/data/language.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,26 +45,18 @@
"en": "Edit on GitHub"
}
},
"octopus_markdown_links": {
"label": {
"en": "Use Octopus docs with AI"
},
"view_raw": {
"en": "Open this page as markdown"
},
"download_all": {
"en": "Open all docs as markdown"
}
},
"octopus_copy_md": {
"label": {
"en": "Copy as markdown"
},
"copied": {
"en": "Copied"
},
"error": {
"en": "Copy failed"
"view_raw": {
"en": "Open this page as markdown"
},
"more_options": {
"en": "More markdown options"
}
},
"octopus_open_in_llm": {
Expand Down
2 changes: 0 additions & 2 deletions src/layouts/Default.astro
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import Feedback from '../components/Feedback.astro';
import TopNav from '../components/TopNav.astro';
import EditOnGithub from '../components/EditOnGithub.astro';
import CopyAsMarkdown from '../components/CopyAsMarkdown.astro';
import MarkdownLinks from '../components/MarkdownLinks.astro';
import OpenInLlm from '../components/OpenInLlm.astro';
import Plausible from 'src/components/Plausible.astro';
import Footer from 'src/components/Footer.astro';
Expand Down Expand Up @@ -95,7 +94,6 @@ const lastUpdated = frontmatter.modDate ?? frontmatter.pubDate ?? null;
<slot />
<Authors frontmatter={frontmatter} lang={lang} />
<Taxonomy frontmatter={frontmatter} lang={lang} />
<MarkdownLinks lang={lang} />
</div>
</article>
</main>
Expand Down
39 changes: 20 additions & 19 deletions src/scripts/copy-as-markdown.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
import { copyOnClick, revertAfter } from './modules/clipboard.js';

// Which label shows, and which glyph the icon masks, is CSS's decision - all
// three labels are in the DOM, and this only says which state the button is in.
function showLabelResult(button: HTMLElement, ok: boolean): string {
const state = ok ? 'copied' : 'failed';
// Which label shows, and which glyph the icon masks, is CSS's decision - both
// labels are in the DOM, and this only says whether the copy landed.
//
// A copy that fails says nothing. Writing text to the clipboard is not something
// a reader can act on the failure of, and the chain in clipboard.js already logs
// whichever rung refused.
function showCopied(button: HTMLElement, ok: boolean): string {
if (!ok) return '';

// Both are cleared every time, so a failure followed by a success inside the
// revert window does not leave the button wearing two states at once.
const rest = () => {
delete button.dataset.copied;
delete button.dataset.failed;
};
button.dataset.copied = '';
revertAfter(button, () => delete button.dataset.copied);

rest();
button.dataset[state] = '';
revertAfter(button, rest);

const label = button.querySelector<HTMLElement>(
`.octo-copy-md__label--${state}`
);
const label = button.querySelector<HTMLElement>('.octo-copy-md__copied');
return label?.textContent?.trim() ?? '';
}

// Handed over unresolved: the clipboard write starts while the page is still
// downloading, which is what keeps the copy working in Safari.
function pageMarkdown(button: HTMLElement): Promise<string> | null {
const url = button.dataset.copyMdUrl;
// The URL sits on the split button wrapping the copy half, so that a click on
// the caret beside it resolves to the menu rather than to a copy.
const url =
button.closest<HTMLElement>('[data-copy-md-url]')?.dataset.copyMdUrl;
if (!url) return null;

return fetch(url).then((response) => {
Expand All @@ -34,4 +31,8 @@ function pageMarkdown(button: HTMLElement): Promise<string> | null {
});
}

copyOnClick('[data-copy-md-url]', pageMarkdown, { show: showLabelResult });
// Matched on the primary half rather than on the control, so that the caret does
// not copy the page on its way to opening the menu.
copyOnClick('[data-copy-md-url] .split-btn__primary', pageMarkdown, {
show: showCopied,
});
1 change: 0 additions & 1 deletion src/scripts/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
addListImageIntersectionObserver,
} from './modules/animation.js';
import { addResizedEvent } from './modules/resizing.js';
import { markdownLinkMenus } from './modules/markdown-links.js';
import { setClickableBlocks } from './modules/click-blocks.js';
import { setExternalLinkAttributes } from './modules/external-links.js';
import { monitorInputType } from './modules/input-type.js';
Expand Down
41 changes: 0 additions & 41 deletions src/scripts/modules/markdown-links.js

This file was deleted.

Loading