Skip to content

Add document color support for format color-hex - #83

Merged
srivastava-diya merged 3 commits into
hyperjump-io:mainfrom
srivastava-diya:document-colors
Sep 8, 2026
Merged

Add document color support for format color-hex#83
srivastava-diya merged 3 commits into
hyperjump-io:mainfrom
srivastava-diya:document-colors

Conversation

@srivastava-diya

Copy link
Copy Markdown
Collaborator

Description

Implements Document Color support, so strings that are marked by a schema as "format": "color-hex" render an inline color swatch and open the editor's color picker. Editing through the picker writes the new hex back into the document.

Screen.Recording.2026-09-03.094002.mp4

Comment on lines +48 to +53
const color = colorFromHex(node.value as string);
if (!color) {
continue;
}

const annotations = await jsonDocument.getAnnotations(node);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

here we Parse first, then check annotations. As regex is cheap and rules out the vast majority of strings before we do the async lookup.

});
};

const HEX_COLOR = /^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

CSS-style hex colors: #rgb, #rgba, #rrggbb, #rrggbbaa.


const HEX_COLOR = /^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;

const colorFromHex = (text: string): Color | undefined => {

@srivastava-diya srivastava-diya Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This helper colorsFromHex converts a hex color string into LSP's Color, whose channels are 0-1 floats rather than 0-255 integers.

  • Shorthand forms (#rgb, #rgba) use one digit per channel, which expands by doubling the digit for eg. "f" becomes "ff".
  • Alpha defaults to fully opaque when the hex doesn't carry one, it is basically an additional parameter that determines if the color has some transparency.

Comment on lines +97 to +99
const toTwoDigitHex = (channel: number): string => {
return Math.round(channel * 255).toString(16)
.padStart(2, "0");

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This converts one 0-1 channel back to a zero-padded hex byte, so 1 becomes "ff" and 0 becomes "00" rather than "f"/"0", which wouldn't be valid hex.

Comment on lines +102 to +104
const hexFromColor = (color: Color): string => {
const hex = `#${toTwoDigitHex(color.red)}${toTwoDigitHex(color.green)}${toTwoDigitHex(color.blue)}`;
return color.alpha === 1 ? hex : `${hex}${toTwoDigitHex(color.alpha)}`;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this converts a color picked in the editor back into a hex string to write into the document. Alpha is omitted when fully opaque, so we produce #rrggbb rather than a needlessly long #rrggbbff.

Comment on lines +85 to +91
const digits = text.slice(1);
const isShorthand = digits.length < 6;
const width = isShorthand ? 1 : 2;
const channel = (index: number): number => {
const digit = digits.slice(index * width, index * width + width);
return parseInt(isShorthand ? digit + digit : digit, 16) / 255;
};

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

we first strip the leading # and then read one channel at a time. Hex colors come in two groups

  • shorthand (#rgb, #rgba) which uses a single digit per channel
  • full form (#rrggbb, #rrggbbaa) which uses two digit per channel
    so width picks how many digits to slice per channel, and anything under 6 digits total is shorthand. For shorthand, each digit is doubled before parsing, since CSS defines #f0a as equivalent to #ff00aa . Finally, dividing by 255 converts the 0–255 byte into the 0–1 float that LSP's Color type expects.

@jdesrosiers jdesrosiers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This one is a little weird because 'color-hex' isn't a format defined by JSON Schema. I'm guessing this is behavior we're trying to match in the vscode json language server, so it's fine.

@srivastava-diya
srivastava-diya merged commit 7d53c86 into hyperjump-io:main Sep 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants