Loading packages/client/src/components/Settings/TemplateSettings.tsx +9 −1 Original line number Diff line number Diff line import { useTemplateContext } from "../../contexts/TemplateContext"; import { Input, Select, SelectItem, Slider } from "@heroui/react"; import { Alert, Input, Select, SelectItem, Slider } from "@heroui/react"; import { Switch } from "../core/Switch"; import { Button } from "../core/Button"; import { toast } from "react-toastify"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faLink } from "@fortawesome/free-solid-svg-icons"; import { WebGLUtils } from "@/lib/webgl"; export const TemplateSettings = () => { const { Loading @@ -29,6 +30,13 @@ export const TemplateSettings = () => { return ( <div className="flex flex-col p-2"> <header className="flex flex-col gap-2"> {!WebGLUtils.detectWebGL() && ( <Alert color="warning"> <strong>WebGL is not available</strong> <br /> <span>Templating uses WebGL</span> </Alert> )} <div className="flex items-center gap-2"> <Switch isSelected={enable || false} Loading packages/client/src/components/Templating/Template.tsx +11 −6 Original line number Diff line number Diff line Loading @@ -3,8 +3,12 @@ import { Template as TemplateCl } from "../../lib/template"; import { useAppContext } from "../../contexts/AppContext"; import { useTemplateContext } from "../../contexts/TemplateContext"; import { CanvasCore } from "../../lib/canvas"; import { WebGLUtils } from "@/lib/webgl"; export const Template = () => { export const Template = () => WebGLUtils.detectWebGL() ? $Template : undefined; const $Template = () => { const { config } = useAppContext(); const { enable, url, width, setWidth, x, y, opacity, setX, setY, style } = useTemplateContext(); Loading Loading @@ -40,7 +44,7 @@ export const Template = () => { if (!startLocation) return; if (!CanvasCore.instance) { console.warn( "[Template#handleMouseMove] Canvas.instance is not defined" "[Template#handleMouseMove] Canvas.instance is not defined", ); return; } Loading @@ -62,10 +66,11 @@ export const Template = () => { const x = parseInt( templateHolderRef.style.getPropertyValue("left").replace("px", "") || "0" "0", ); const y = parseInt( templateHolderRef.style.getPropertyValue("top").replace("px", "") || "0" templateHolderRef.style.getPropertyValue("top").replace("px", "") || "0", ); setX(x); Loading @@ -88,7 +93,7 @@ export const Template = () => { useEffect(() => { if (!instance.current) { console.warn( "[Template] Received template enable but no instance exists" "[Template] Received template enable but no instance exists", ); return; } Loading @@ -105,7 +110,7 @@ export const Template = () => { useEffect(() => { if (!instance.current) { console.warn( "[Template] Recieved template url update but no template instance exists" "[Template] Recieved template url update but no template instance exists", ); return; } Loading packages/client/src/contexts/TemplateContext.tsx +5 −4 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ import { import { type IRouterData, Router } from "../lib/router"; import { KeybindManager } from "../lib/keybinds"; import { type TemplateStyle } from "../lib/template"; import { WebGLUtils } from "@/lib/webgl"; interface ITemplate { /** Loading Loading @@ -54,13 +55,13 @@ export const TemplateContext = ({ children }: PropsWithChildren) => { const [enable, setEnable] = useState(!!routerData.template?.url); const [url, setURL] = useState<string | undefined>(routerData.template?.url); const [width, setWidth] = useState<number | undefined>( routerData.template?.width routerData.template?.width, ); const [x, setX] = useState(routerData.template?.x || 0); const [y, setY] = useState(routerData.template?.y || 0); const [opacity, setOpacity] = useState(100); const [style, setStyle] = useState<TemplateStyle>( routerData.template?.style || "ONE_TO_ONE" routerData.template?.style || "ONE_TO_ONE", ); const [showMobileTools, setShowMobileTools] = useState(true); Loading Loading @@ -103,7 +104,7 @@ export const TemplateContext = ({ children }: PropsWithChildren) => { } else if (Date.now() - initAt.current < 2 * 1000) { console.debug( "TemplateContext updating router too soon after init", Date.now() - initAt.current Date.now() - initAt.current, ); } Loading @@ -114,7 +115,7 @@ export const TemplateContext = ({ children }: PropsWithChildren) => { return ( <templateContext.Provider value={{ enable, enable: WebGLUtils.detectWebGL() && enable, setEnable, url, setURL, Loading packages/client/src/lib/webgl.ts +21 −8 Original line number Diff line number Diff line let hasWebGL: boolean | null = null; /** * Utilities for WebGL contexts */ Loading @@ -8,6 +10,17 @@ export class WebGLUtils { this.context = context; } static detectWebGL() { if (typeof hasWebGL !== "boolean") { const canvas = document.createElement("canvas"); const gl = canvas.getContext("webgl"); hasWebGL = gl instanceof WebGLRenderingContext; } return hasWebGL; } /** * Create WebGL texture * Loading @@ -21,22 +34,22 @@ export class WebGLUtils { this.context.texParameteri( this.context.TEXTURE_2D, this.context.TEXTURE_WRAP_S, this.context.CLAMP_TO_EDGE this.context.CLAMP_TO_EDGE, ); this.context.texParameteri( this.context.TEXTURE_2D, this.context.TEXTURE_WRAP_T, this.context.CLAMP_TO_EDGE this.context.CLAMP_TO_EDGE, ); this.context.texParameteri( this.context.TEXTURE_2D, this.context.TEXTURE_MIN_FILTER, this.context.NEAREST this.context.NEAREST, ); this.context.texParameteri( this.context.TEXTURE_2D, this.context.TEXTURE_MAG_FILTER, this.context.NEAREST this.context.NEAREST, ); return texture; } Loading @@ -56,16 +69,16 @@ export class WebGLUtils { const program = this.context.createProgram()!; this.context.attachShader( program, this.createShader(this.context.VERTEX_SHADER, vertexSource) this.createShader(this.context.VERTEX_SHADER, vertexSource), ); this.context.attachShader( program, this.createShader(this.context.FRAGMENT_SHADER, fragmentSource) this.createShader(this.context.FRAGMENT_SHADER, fragmentSource), ); this.context.linkProgram(program); if (!this.context.getProgramParameter(program, this.context.LINK_STATUS)) { throw new Error( `Failed to link WebGL template program:\n\n${this.context.getProgramInfoLog(program)}` `Failed to link WebGL template program:\n\n${this.context.getProgramInfoLog(program)}`, ); } return program; Loading @@ -90,7 +103,7 @@ export class WebGLUtils { if (!this.context.getShaderParameter(shader, this.context.COMPILE_STATUS)) { throw new Error( `Failed to compile WebGL template shader:\n\n${this.context.getShaderInfoLog(shader)}` `Failed to compile WebGL template shader:\n\n${this.context.getShaderInfoLog(shader)}`, ); } Loading Loading
packages/client/src/components/Settings/TemplateSettings.tsx +9 −1 Original line number Diff line number Diff line import { useTemplateContext } from "../../contexts/TemplateContext"; import { Input, Select, SelectItem, Slider } from "@heroui/react"; import { Alert, Input, Select, SelectItem, Slider } from "@heroui/react"; import { Switch } from "../core/Switch"; import { Button } from "../core/Button"; import { toast } from "react-toastify"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faLink } from "@fortawesome/free-solid-svg-icons"; import { WebGLUtils } from "@/lib/webgl"; export const TemplateSettings = () => { const { Loading @@ -29,6 +30,13 @@ export const TemplateSettings = () => { return ( <div className="flex flex-col p-2"> <header className="flex flex-col gap-2"> {!WebGLUtils.detectWebGL() && ( <Alert color="warning"> <strong>WebGL is not available</strong> <br /> <span>Templating uses WebGL</span> </Alert> )} <div className="flex items-center gap-2"> <Switch isSelected={enable || false} Loading
packages/client/src/components/Templating/Template.tsx +11 −6 Original line number Diff line number Diff line Loading @@ -3,8 +3,12 @@ import { Template as TemplateCl } from "../../lib/template"; import { useAppContext } from "../../contexts/AppContext"; import { useTemplateContext } from "../../contexts/TemplateContext"; import { CanvasCore } from "../../lib/canvas"; import { WebGLUtils } from "@/lib/webgl"; export const Template = () => { export const Template = () => WebGLUtils.detectWebGL() ? $Template : undefined; const $Template = () => { const { config } = useAppContext(); const { enable, url, width, setWidth, x, y, opacity, setX, setY, style } = useTemplateContext(); Loading Loading @@ -40,7 +44,7 @@ export const Template = () => { if (!startLocation) return; if (!CanvasCore.instance) { console.warn( "[Template#handleMouseMove] Canvas.instance is not defined" "[Template#handleMouseMove] Canvas.instance is not defined", ); return; } Loading @@ -62,10 +66,11 @@ export const Template = () => { const x = parseInt( templateHolderRef.style.getPropertyValue("left").replace("px", "") || "0" "0", ); const y = parseInt( templateHolderRef.style.getPropertyValue("top").replace("px", "") || "0" templateHolderRef.style.getPropertyValue("top").replace("px", "") || "0", ); setX(x); Loading @@ -88,7 +93,7 @@ export const Template = () => { useEffect(() => { if (!instance.current) { console.warn( "[Template] Received template enable but no instance exists" "[Template] Received template enable but no instance exists", ); return; } Loading @@ -105,7 +110,7 @@ export const Template = () => { useEffect(() => { if (!instance.current) { console.warn( "[Template] Recieved template url update but no template instance exists" "[Template] Recieved template url update but no template instance exists", ); return; } Loading
packages/client/src/contexts/TemplateContext.tsx +5 −4 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ import { import { type IRouterData, Router } from "../lib/router"; import { KeybindManager } from "../lib/keybinds"; import { type TemplateStyle } from "../lib/template"; import { WebGLUtils } from "@/lib/webgl"; interface ITemplate { /** Loading Loading @@ -54,13 +55,13 @@ export const TemplateContext = ({ children }: PropsWithChildren) => { const [enable, setEnable] = useState(!!routerData.template?.url); const [url, setURL] = useState<string | undefined>(routerData.template?.url); const [width, setWidth] = useState<number | undefined>( routerData.template?.width routerData.template?.width, ); const [x, setX] = useState(routerData.template?.x || 0); const [y, setY] = useState(routerData.template?.y || 0); const [opacity, setOpacity] = useState(100); const [style, setStyle] = useState<TemplateStyle>( routerData.template?.style || "ONE_TO_ONE" routerData.template?.style || "ONE_TO_ONE", ); const [showMobileTools, setShowMobileTools] = useState(true); Loading Loading @@ -103,7 +104,7 @@ export const TemplateContext = ({ children }: PropsWithChildren) => { } else if (Date.now() - initAt.current < 2 * 1000) { console.debug( "TemplateContext updating router too soon after init", Date.now() - initAt.current Date.now() - initAt.current, ); } Loading @@ -114,7 +115,7 @@ export const TemplateContext = ({ children }: PropsWithChildren) => { return ( <templateContext.Provider value={{ enable, enable: WebGLUtils.detectWebGL() && enable, setEnable, url, setURL, Loading
packages/client/src/lib/webgl.ts +21 −8 Original line number Diff line number Diff line let hasWebGL: boolean | null = null; /** * Utilities for WebGL contexts */ Loading @@ -8,6 +10,17 @@ export class WebGLUtils { this.context = context; } static detectWebGL() { if (typeof hasWebGL !== "boolean") { const canvas = document.createElement("canvas"); const gl = canvas.getContext("webgl"); hasWebGL = gl instanceof WebGLRenderingContext; } return hasWebGL; } /** * Create WebGL texture * Loading @@ -21,22 +34,22 @@ export class WebGLUtils { this.context.texParameteri( this.context.TEXTURE_2D, this.context.TEXTURE_WRAP_S, this.context.CLAMP_TO_EDGE this.context.CLAMP_TO_EDGE, ); this.context.texParameteri( this.context.TEXTURE_2D, this.context.TEXTURE_WRAP_T, this.context.CLAMP_TO_EDGE this.context.CLAMP_TO_EDGE, ); this.context.texParameteri( this.context.TEXTURE_2D, this.context.TEXTURE_MIN_FILTER, this.context.NEAREST this.context.NEAREST, ); this.context.texParameteri( this.context.TEXTURE_2D, this.context.TEXTURE_MAG_FILTER, this.context.NEAREST this.context.NEAREST, ); return texture; } Loading @@ -56,16 +69,16 @@ export class WebGLUtils { const program = this.context.createProgram()!; this.context.attachShader( program, this.createShader(this.context.VERTEX_SHADER, vertexSource) this.createShader(this.context.VERTEX_SHADER, vertexSource), ); this.context.attachShader( program, this.createShader(this.context.FRAGMENT_SHADER, fragmentSource) this.createShader(this.context.FRAGMENT_SHADER, fragmentSource), ); this.context.linkProgram(program); if (!this.context.getProgramParameter(program, this.context.LINK_STATUS)) { throw new Error( `Failed to link WebGL template program:\n\n${this.context.getProgramInfoLog(program)}` `Failed to link WebGL template program:\n\n${this.context.getProgramInfoLog(program)}`, ); } return program; Loading @@ -90,7 +103,7 @@ export class WebGLUtils { if (!this.context.getShaderParameter(shader, this.context.COMPILE_STATUS)) { throw new Error( `Failed to compile WebGL template shader:\n\n${this.context.getShaderInfoLog(shader)}` `Failed to compile WebGL template shader:\n\n${this.context.getShaderInfoLog(shader)}`, ); } Loading