mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 15:28:04 +08:00
Improve tooltip docs with Markdown styling and refined math node explanations (#3488)
This commit is contained in:
@@ -27,7 +27,7 @@
|
||||
// State provider systems
|
||||
let dialog = createDialogState(editor);
|
||||
setContext("dialog", dialog);
|
||||
let tooltip = createTooltipState();
|
||||
let tooltip = createTooltipState(editor);
|
||||
setContext("tooltip", tooltip);
|
||||
let document = createDocumentState(editor);
|
||||
setContext("document", document);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { onDestroy, createEventDispatcher } from "svelte";
|
||||
import { getContext, onDestroy, createEventDispatcher } from "svelte";
|
||||
|
||||
import type { HSV, RGB, FillChoice } from "@graphite/messages";
|
||||
import type { MenuDirection } from "@graphite/messages";
|
||||
import type { HSV, RGB, FillChoice, MenuDirection } from "@graphite/messages";
|
||||
import { Color, contrastingOutlineFactor, Gradient } from "@graphite/messages";
|
||||
import type { TooltipState } from "@graphite/state-providers/tooltip";
|
||||
import { clamp } from "@graphite/utility-functions/math";
|
||||
|
||||
import FloatingMenu from "@graphite/components/layout/FloatingMenu.svelte";
|
||||
@@ -40,6 +40,7 @@
|
||||
];
|
||||
|
||||
const dispatch = createEventDispatcher<{ colorOrGradient: FillChoice; startHistoryTransaction: undefined }>();
|
||||
const tooltip = getContext<TooltipState>("tooltip");
|
||||
|
||||
export let colorOrGradient: FillChoice;
|
||||
export let allowNone = false;
|
||||
@@ -424,12 +425,16 @@
|
||||
"--opaque-color-contrasting": (newColor.opaque() || new Color(0, 0, 0, 1)).contrastingColor(),
|
||||
}}
|
||||
>
|
||||
{@const hueDescription = "The shade along the spectrum of the rainbow."}
|
||||
{@const saturationDescription = "The vividness from grayscale to full color."}
|
||||
{@const valueDescription = "The brightness from black to full color."}
|
||||
<LayoutCol class="pickers-and-gradient">
|
||||
<LayoutRow class="pickers">
|
||||
<LayoutCol
|
||||
class="saturation-value-picker"
|
||||
data-tooltip-label="Saturation and Value"
|
||||
data-tooltip-description={disabled ? "Disabled (read-only)." : ""}
|
||||
data-tooltip-description={`To move only along the saturation (X) or value (Y) axis, perform the shortcut shown.${disabled ? "\n\nDisabled (read-only)." : ""}`}
|
||||
data-tooltip-shortcut={$tooltip.shiftClickShortcut?.shortcut ? JSON.stringify($tooltip.shiftClickShortcut.shortcut) : undefined}
|
||||
on:pointerdown={onPointerDown}
|
||||
data-saturation-value-picker
|
||||
>
|
||||
@@ -449,7 +454,7 @@
|
||||
<LayoutCol
|
||||
class="hue-picker"
|
||||
data-tooltip-label="Hue"
|
||||
data-tooltip-description={`The shade along the spectrum of the rainbow.${disabled ? "\n\nDisabled (read-only)." : ""}`}
|
||||
data-tooltip-description={`${hueDescription}${disabled ? "\n\nDisabled (read-only)." : ""}`}
|
||||
on:pointerdown={onPointerDown}
|
||||
data-hue-picker
|
||||
>
|
||||
@@ -522,10 +527,8 @@
|
||||
</LayoutRow>
|
||||
<!-- <DropdownInput entries={[[{ label: "sRGB" }]]} selectedIndex={0} disabled={true} tooltipDescription="Color model, color space, and HDR (coming soon)." /> -->
|
||||
<LayoutRow>
|
||||
<TextLabel
|
||||
tooltipLabel="Hex Color Code"
|
||||
tooltipDescription="Color code in hexadecimal format. 6 digits if opaque, 8 with alpha.\nAccepts input of CSS color values including named colors.">Hex</TextLabel
|
||||
>
|
||||
{@const hexDescription = "Color code in hexadecimal format. 6 digits if opaque, 8 with alpha. Accepts input of CSS color values including named colors."}
|
||||
<TextLabel tooltipLabel="Hex Color Code" tooltipDescription={hexDescription}>Hex</TextLabel>
|
||||
<Separator type="Related" />
|
||||
<LayoutRow>
|
||||
<TextInput
|
||||
@@ -537,7 +540,7 @@
|
||||
}}
|
||||
centered={true}
|
||||
tooltipLabel="Hex Color Code"
|
||||
tooltipDescription="Color code in hexadecimal format. 6 digits if opaque, 8 with alpha.\nAccepts input of CSS color values including named colors."
|
||||
tooltipDescription={hexDescription}
|
||||
bind:this={hexCodeInputWidget}
|
||||
/>
|
||||
</LayoutRow>
|
||||
@@ -601,16 +604,17 @@
|
||||
v: "Value Component",
|
||||
}[channel]}
|
||||
tooltipDescription={{
|
||||
h: "The shade along the spectrum of the rainbow.",
|
||||
s: "The vividness from grayscale to full color.",
|
||||
v: "The brightness from black to full color.",
|
||||
h: hueDescription,
|
||||
s: saturationDescription,
|
||||
v: valueDescription,
|
||||
}[channel]}
|
||||
/>
|
||||
{/each}
|
||||
</LayoutRow>
|
||||
</LayoutRow>
|
||||
<LayoutRow>
|
||||
<TextLabel tooltipLabel="Alpha" tooltipDescription="The level of translucency, from transparent (0%) to opaque (100%).">Alpha</TextLabel>
|
||||
{@const alphaDescription = "The level of translucency, from transparent (0%) to opaque (100%)."}
|
||||
<TextLabel tooltipLabel="Alpha" tooltipDescription={alphaDescription}>Alpha</TextLabel>
|
||||
<Separator type="Related" />
|
||||
<NumberInput
|
||||
value={!isNone ? alpha * 100 : undefined}
|
||||
@@ -630,7 +634,7 @@
|
||||
mode="Range"
|
||||
displayDecimalPlaces={1}
|
||||
tooltipLabel="Alpha"
|
||||
tooltipDescription="The level of translucency, from transparent (0%) to opaque (100%)."
|
||||
tooltipDescription={alphaDescription}
|
||||
/>
|
||||
</LayoutRow>
|
||||
<LayoutRow class="leftover-space" />
|
||||
@@ -670,7 +674,7 @@
|
||||
data-pure-tile={name.toLowerCase()}
|
||||
style:--pure-color={color}
|
||||
style:--pure-color-gray={gray}
|
||||
data-tooltip-label="Set to Red"
|
||||
data-tooltip-label={`Set to ${name}`}
|
||||
data-tooltip-description={disabled ? "Disabled (read-only)." : ""}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
|
||||
let self: FloatingMenu | undefined;
|
||||
|
||||
$: label = filterTodo($tooltip.element?.getAttribute("data-tooltip-label")?.trim());
|
||||
$: description = filterTodo($tooltip.element?.getAttribute("data-tooltip-description")?.trim());
|
||||
$: label = parseMarkdown(filterTodo($tooltip.element?.getAttribute("data-tooltip-label")?.trim()));
|
||||
$: description = parseMarkdown(filterTodo($tooltip.element?.getAttribute("data-tooltip-description")?.trim()));
|
||||
$: shortcutJSON = $tooltip.element?.getAttribute("data-tooltip-shortcut")?.trim();
|
||||
$: shortcut = ((shortcutJSON) => {
|
||||
if (!shortcutJSON) return undefined;
|
||||
@@ -32,6 +32,26 @@
|
||||
if (text?.trim().toUpperCase() === "TODO" && !editor.handle.inDevelopmentMode()) return "";
|
||||
return text;
|
||||
}
|
||||
|
||||
function parseMarkdown(markdown: string | undefined): string | undefined {
|
||||
if (!markdown) return undefined;
|
||||
|
||||
return (
|
||||
markdown
|
||||
// .split("\n")
|
||||
// .map((line) => line.trim())
|
||||
// .join("\n")
|
||||
// .split("\n\n")
|
||||
// .map((paragraph) => paragraph.replaceAll("\n", " "))
|
||||
// .join("\n\n")
|
||||
// Bold
|
||||
.replace(/\*\*((?:(?!\*\*).)+)\*\*/g, "<strong>$1</strong>")
|
||||
// Italic
|
||||
.replace(/\*([^*]+)\*/g, "<em>$1</em>")
|
||||
// Backticks
|
||||
.replace(/`([^`]+)`/g, "<code>$1</code>")
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if label || description}
|
||||
@@ -40,7 +60,7 @@
|
||||
{#if label || shortcut}
|
||||
<LayoutRow class="tooltip-header">
|
||||
{#if label}
|
||||
<TextLabel class="tooltip-label">{label}</TextLabel>
|
||||
<TextLabel class="tooltip-label">{@html label}</TextLabel>
|
||||
{/if}
|
||||
{#if shortcut}
|
||||
<ShortcutLabel shortcut={{ shortcut }} />
|
||||
@@ -48,7 +68,7 @@
|
||||
</LayoutRow>
|
||||
{/if}
|
||||
{#if description}
|
||||
<TextLabel class="tooltip-description">{description}</TextLabel>
|
||||
<TextLabel class="tooltip-description">{@html description}</TextLabel>
|
||||
{/if}
|
||||
</FloatingMenu>
|
||||
</div>
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
UpdateLayersPanelControlBarLeftLayout,
|
||||
UpdateLayersPanelControlBarRightLayout,
|
||||
UpdateLayersPanelBottomBarLayout,
|
||||
SendShortcutAltClick,
|
||||
} from "@graphite/messages";
|
||||
import type { ActionShortcut, DataBuffer, LayerPanelEntry, Layout } from "@graphite/messages";
|
||||
import type { DataBuffer, LayerPanelEntry, Layout } from "@graphite/messages";
|
||||
import type { NodeGraphState } from "@graphite/state-providers/node-graph";
|
||||
import type { TooltipState } from "@graphite/state-providers/tooltip";
|
||||
import { operatingSystem } from "@graphite/utility-functions/platform";
|
||||
import { extractPixelData } from "@graphite/utility-functions/rasterization";
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
|
||||
const editor = getContext<Editor>("editor");
|
||||
const nodeGraph = getContext<NodeGraphState>("nodeGraph");
|
||||
const tooltip = getContext<TooltipState>("tooltip");
|
||||
|
||||
let list: LayoutCol | undefined;
|
||||
|
||||
@@ -73,13 +74,7 @@
|
||||
let layersPanelControlBarRightLayout: Layout = [];
|
||||
let layersPanelBottomBarLayout: Layout = [];
|
||||
|
||||
let altClickShortcut: ActionShortcut | undefined;
|
||||
|
||||
onMount(() => {
|
||||
editor.subscriptions.subscribeJsMessage(SendShortcutAltClick, async (data) => {
|
||||
altClickShortcut = data.shortcut;
|
||||
});
|
||||
|
||||
editor.subscriptions.subscribeJsMessage(UpdateLayersPanelControlBarLeftLayout, (updateLayersPanelControlBarLeftLayout) => {
|
||||
patchLayout(layersPanelControlBarLeftLayout, updateLayersPanelControlBarLeftLayout);
|
||||
layersPanelControlBarLeftLayout = layersPanelControlBarLeftLayout;
|
||||
@@ -628,7 +623,7 @@
|
||||
? "Hide the layers nested within. (To affect all open descendants, perform the shortcut shown.)"
|
||||
: "Show the layers nested within. (To affect all closed descendants, perform the shortcut shown.)") +
|
||||
(listing.entry.ancestorOfSelected && !listing.entry.expanded ? "\n\nA selected layer is currently contained within.\n" : "")}
|
||||
data-tooltip-shortcut={altClickShortcut?.shortcut ? JSON.stringify(altClickShortcut.shortcut) : undefined}
|
||||
data-tooltip-shortcut={$tooltip.altClickShortcut?.shortcut ? JSON.stringify($tooltip.altClickShortcut.shortcut) : undefined}
|
||||
on:click={(e) => handleExpandArrowClickWithModifiers(e, listing.entry.id)}
|
||||
tabindex="0"
|
||||
></button>
|
||||
@@ -639,8 +634,9 @@
|
||||
<IconLabel
|
||||
icon="Clipped"
|
||||
class="clipped-arrow"
|
||||
tooltipDescription="Clipping mask is active. To release it, perform the shortcut on the layer border."
|
||||
tooltipShortcut={altClickShortcut}
|
||||
tooltipLabel="Layer Clipped"
|
||||
tooltipDescription="Clipping mask is active. To release it, target the bottom border of the layer and perform the shortcut shown."
|
||||
tooltipShortcut={$tooltip.altClickShortcut}
|
||||
/>
|
||||
{/if}
|
||||
<div class="thumbnail">
|
||||
|
||||
@@ -501,7 +501,7 @@
|
||||
style:--data-color-dim={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()}-dim)`}
|
||||
style:--layer-area-width={layerAreaWidth}
|
||||
style:--node-chain-area-left-extension={layerChainWidth !== 0 ? layerChainWidth + 0.5 : 0}
|
||||
data-tooltip-label={node.displayName === node.reference ? node.displayName : `${node.displayName} (${node.reference})`}
|
||||
data-tooltip-label={node.displayName === node.reference || !node.reference ? node.displayName : `${node.displayName} (${node.reference})`}
|
||||
data-tooltip-description={`
|
||||
${(description || "").trim()}${editor.handle.inDevelopmentMode() ? `\n\nID: ${node.id}. Position: (${node.position.x}, ${node.position.y}).` : ""}
|
||||
`.trim()}
|
||||
@@ -651,7 +651,7 @@
|
||||
style:--clip-path-id={`url(#${clipPathId})`}
|
||||
style:--data-color={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()})`}
|
||||
style:--data-color-dim={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()}-dim)`}
|
||||
data-tooltip-label={node.displayName === node.reference ? node.displayName : `${node.displayName} (${node.reference})`}
|
||||
data-tooltip-label={node.displayName === node.reference || !node.reference ? node.displayName : `${node.displayName} (${node.reference})`}
|
||||
data-tooltip-description={`
|
||||
${(description || "").trim()}${editor.handle.inDevelopmentMode() ? `\n\nID: ${node.id}. Position: (${node.position.x}, ${node.position.y}).` : ""}
|
||||
`.trim()}
|
||||
|
||||
@@ -72,7 +72,8 @@
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
&.monospace {
|
||||
&.monospace,
|
||||
code {
|
||||
font-family: "Source Code Pro", monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
@@ -94,5 +95,10 @@
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
code {
|
||||
background: var(--color-3-darkgray);
|
||||
padding: 0 2px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,24 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from "svelte";
|
||||
import { getContext } from "svelte";
|
||||
|
||||
import type { Editor } from "@graphite/editor";
|
||||
import type { ActionShortcut } from "@graphite/messages";
|
||||
import { SendShortcutF11 } from "@graphite/messages";
|
||||
import type { FullscreenState } from "@graphite/state-providers/fullscreen";
|
||||
|
||||
import type { TooltipState } from "@graphite/state-providers/tooltip";
|
||||
|
||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
||||
|
||||
const fullscreen = getContext<FullscreenState>("fullscreen");
|
||||
const editor = getContext<Editor>("editor");
|
||||
|
||||
let f11Shortcut: ActionShortcut | undefined = undefined;
|
||||
|
||||
onMount(() => {
|
||||
editor.subscriptions.subscribeJsMessage(SendShortcutF11, async (data) => {
|
||||
f11Shortcut = data.shortcut;
|
||||
});
|
||||
});
|
||||
const tooltip = getContext<TooltipState>("tooltip");
|
||||
|
||||
async function handleClick() {
|
||||
if ($fullscreen.windowFullscreen) fullscreen.exitFullscreen();
|
||||
@@ -31,7 +22,7 @@
|
||||
on:click={handleClick}
|
||||
tooltipLabel={$fullscreen.windowFullscreen ? "Exit Fullscreen" : "Enter Fullscreen"}
|
||||
tooltipDescription={$fullscreen.keyboardLockApiSupported ? "While fullscreen, keyboard shortcuts normally reserved by the browser become available." : ""}
|
||||
tooltipShortcut={f11Shortcut}
|
||||
tooltipShortcut={$tooltip.f11Shortcut}
|
||||
>
|
||||
<IconLabel icon={$fullscreen.windowFullscreen ? "FullscreenExit" : "FullscreenEnter"} />
|
||||
</LayoutRow>
|
||||
|
||||
@@ -481,11 +481,11 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||
The browser's clipboard permission has been denied.
|
||||
|
||||
Open the browser's website settings (usually accessible
|
||||
just left of the URL) to allow this permission.
|
||||
just left of the URL bar) to allow this permission.
|
||||
`;
|
||||
const nothing = stripIndents`
|
||||
No valid clipboard data was found. You may have better
|
||||
luck pasting with the standard keyboard shortcut instead.
|
||||
success pasting with the standard keyboard shortcut instead.
|
||||
`;
|
||||
|
||||
const matchMessage = {
|
||||
|
||||
@@ -119,6 +119,11 @@ export class SendShortcutAltClick extends JsMessage {
|
||||
readonly shortcut!: ActionShortcut | undefined;
|
||||
}
|
||||
|
||||
export class SendShortcutShiftClick extends JsMessage {
|
||||
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||
readonly shortcut!: ActionShortcut | undefined;
|
||||
}
|
||||
|
||||
export class UpdateNodeThumbnail extends JsMessage {
|
||||
readonly id!: bigint;
|
||||
|
||||
@@ -1696,6 +1701,7 @@ export const messageMakers: Record<string, MessageMaker> = {
|
||||
SendUIMetadata,
|
||||
SendShortcutF11,
|
||||
SendShortcutAltClick,
|
||||
SendShortcutShiftClick,
|
||||
TriggerAboutGraphiteLocalizedCommitDate,
|
||||
TriggerDisplayThirdPartyLicensesDialog,
|
||||
TriggerExportImage,
|
||||
|
||||
@@ -9,7 +9,7 @@ export function createAppWindowState(editor: Editor) {
|
||||
maximized: false,
|
||||
fullscreen: false,
|
||||
viewportHolePunch: false,
|
||||
uiScale: 1.0,
|
||||
uiScale: 1,
|
||||
});
|
||||
|
||||
// Set up message subscriptions on creation
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
import { type Editor } from "@graphite/editor";
|
||||
import { SendShortcutAltClick, SendShortcutF11, SendShortcutShiftClick, type ActionShortcut } from "@graphite/messages";
|
||||
|
||||
const SHOW_TOOLTIP_DELAY_MS = 500;
|
||||
|
||||
export function createTooltipState() {
|
||||
export function createTooltipState(editor: Editor) {
|
||||
const { subscribe, update } = writable({
|
||||
visible: false,
|
||||
element: undefined as Element | undefined,
|
||||
position: { x: 0, y: 0 },
|
||||
shiftClickShortcut: undefined as ActionShortcut | undefined,
|
||||
altClickShortcut: undefined as ActionShortcut | undefined,
|
||||
f11Shortcut: undefined as ActionShortcut | undefined,
|
||||
});
|
||||
|
||||
let tooltipTimeout: ReturnType<typeof setTimeout> | undefined = undefined;
|
||||
@@ -45,6 +51,25 @@ export function createTooltipState() {
|
||||
}, SHOW_TOOLTIP_DELAY_MS);
|
||||
});
|
||||
|
||||
editor.subscriptions.subscribeJsMessage(SendShortcutShiftClick, async (data) => {
|
||||
update((state) => {
|
||||
state.shiftClickShortcut = data.shortcut;
|
||||
return state;
|
||||
});
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(SendShortcutAltClick, async (data) => {
|
||||
update((state) => {
|
||||
state.altClickShortcut = data.shortcut;
|
||||
return state;
|
||||
});
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(SendShortcutF11, async (data) => {
|
||||
update((state) => {
|
||||
state.f11Shortcut = data.shortcut;
|
||||
return state;
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener("mousedown", closeTooltip);
|
||||
document.addEventListener("keydown", closeTooltip);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user