Skip to content
This repository has been archived by the owner on Nov 7, 2024. It is now read-only.

Commit

Permalink
feat(invert-colors): add normal-image option
Browse files Browse the repository at this point in the history
  • Loading branch information
SetsuikiHyoryu committed Jun 27, 2024
1 parent 2076b05 commit a7e1277
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 17 deletions.
4 changes: 3 additions & 1 deletion addons/frontend/src/typst.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ body {
transition: filter 0.1s ease-in-out;
}

#typst-app.invert-colors .typst-image:hover {
#typst-app.invert-colors .typst-image:hover,
#typst-app.invert-colors.normal-image .typst-image {
filter: invert(1) hue-rotate(180deg);
}

.hide-scrollbar-x {
overflow-x: hidden;
}
Expand Down
137 changes: 121 additions & 16 deletions addons/frontend/src/ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,28 +393,133 @@ export async function wsMain({ url, previewMode, isContentPreview }: WsArgs) {
}));
};

function ensureInvertColors(root: HTMLElement | null, strategy: string) {
/** The strategy to set invert colors */
const INVERT_COLORS_STRATEGY = {
/** Disable color inversion of the preview */
NEVER: 'never',
/** Invert colors smartly by detecting dark/light themes in browser environment or by `typst query` your document */
AUTO: 'auto',
/** Always invert colors of the preview */
ALWAYS: 'always'
} as const

/** The value of strategy constant */
type StrategyKey = typeof INVERT_COLORS_STRATEGY[keyof typeof INVERT_COLORS_STRATEGY]
/** The map of strategy */
type StrategyMap = Record<StrategyKey, TargetKey[]>

/** The target for which to invert colors */
const INVERT_COLORS_TARGET = {
/** All elements */
ALL: 'all',
/** The image */
IMAGE: 'image'
} as const

/** The value of target constant */
type TargetKey = typeof INVERT_COLORS_TARGET[keyof typeof INVERT_COLORS_TARGET]
/** The map of target */
type TargetMap = Record<TargetKey, StrategyKey>

/** Strategy for ensure invert colors */
type Strategy = string | TargetMap | StrategyMap
function ensureInvertColors(root: HTMLElement | null, strategy: Strategy) {
if (!root) {
return;
}

let needInvertColor = false;
switch (strategy) {
case "never":
default:
break;
case "auto":
needInvertColor = determineInvertColor();
break;
case "always":
needInvertColor = true;
break;
let [needInvertColor, needInvertImage] = [false, false]

const isStrategyMap = Object.keys(strategy).every(
item => Object.values(INVERT_COLORS_STRATEGY).includes(item as StrategyKey)
)

const isTargetMap = Object.keys(strategy).every(
item => Object.values(INVERT_COLORS_TARGET).includes(item as TargetKey)
)

if (typeof strategy === 'string') {
needInvertColor = handleInvertColorsByStringStrategy(strategy, determineInvertColor)
} else if (isStrategyMap) {
[needInvertColor, needInvertImage] = handleInvertColorsByStrategyMap(
strategy as StrategyMap,
determineInvertColor
)
} else if (isTargetMap) {
[needInvertColor, needInvertImage] = handleInvertColorsByTargetMap(
strategy as TargetMap,
determineInvertColor
)
}

root.classList.toggle("invert-colors", needInvertColor)
root.classList.toggle("normal-image", !needInvertImage)

/**
* Handle invert colors mode based on a string value.
* @param strategy - The mode user setted.
* @param autoComputer - The function to compute the mode for 'auto'.
* @returns needInvertColor - Use or not use invert color.
*/
function handleInvertColorsByStringStrategy(strategy: string, autoComputer: () => boolean): boolean {
let needInvertColor = false;

switch (strategy) {
case INVERT_COLORS_STRATEGY.NEVER:
default:
break;
case INVERT_COLORS_STRATEGY.AUTO:
needInvertColor = autoComputer();
break;
case INVERT_COLORS_STRATEGY.ALWAYS:
needInvertColor = true;
break;
}

return needInvertColor
}

/**
* Handle invert colors mode based on a target map.
* @param strategy - The mode user setted.
* @param autoComputer - The function to compute the mode for 'auto'.
* @returns [needInvertColor, needInvertImage] - Use or not use invert color for targets.
*/
function handleInvertColorsByTargetMap(strategy: TargetMap, autoComputer: () => boolean): [boolean, boolean] {
const invertColorStrategyMap: Record<StrategyKey, boolean> = {
[INVERT_COLORS_STRATEGY.NEVER]: false,
[INVERT_COLORS_STRATEGY.AUTO]: autoComputer(),
[INVERT_COLORS_STRATEGY.ALWAYS]: true,
}

let [needInvertColor, needInvertImage] = [
invertColorStrategyMap[strategy.all],
invertColorStrategyMap[strategy.image]
]

return [needInvertColor, needInvertImage]
}

if (needInvertColor) {
root.classList.add("invert-colors");
} else {
root.classList.remove("invert-colors");
/**
* Handle invert colors mode based on a strategy map.
* @param strategy - The mode user setted.
* @param autoComputer - The function to compute the mode for 'auto'.
* @returns [needInvertColor, needInvertImage] - Use or not use invert color for targets.
*/
function handleInvertColorsByStrategyMap(strategy: StrategyMap, autoComputer: () => boolean): [boolean, boolean] {
const invertColorTargetMap = {
[INVERT_COLORS_TARGET.ALL]: false,
[INVERT_COLORS_TARGET.IMAGE]: false
}

strategy.always?.forEach(item => invertColorTargetMap[item] = true)
strategy.auto?.forEach(item => invertColorTargetMap[item] = autoComputer())
strategy.never?.forEach(item => invertColorTargetMap[item] = false)

return [
invertColorTargetMap[INVERT_COLORS_TARGET.ALL],
invertColorTargetMap[INVERT_COLORS_TARGET.IMAGE]
]
}

function determineInvertColor() {
Expand Down

0 comments on commit a7e1277

Please sign in to comment.