mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
background support (from metadata). colors, gradients, images. (#168)
This commit is contained in:
parent
6e75c2cdfd
commit
551802dbd7
@ -9,10 +9,10 @@ body {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: var(--main-bg-color);
|
|
||||||
color: var(--main-text-color);
|
color: var(--main-text-color);
|
||||||
font: var(--base-font);
|
font: var(--base-font);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
background: var(--main-bg-color);
|
||||||
-webkit-font-smoothing: auto;
|
-webkit-font-smoothing: auto;
|
||||||
backface-visibility: hidden;
|
backface-visibility: hidden;
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
@ -51,6 +51,15 @@ body {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
.app-background {
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-boundary {
|
.error-boundary {
|
||||||
|
@ -8,6 +8,7 @@ import * as services from "@/store/services";
|
|||||||
import * as keyutil from "@/util/keyutil";
|
import * as keyutil from "@/util/keyutil";
|
||||||
import * as layoututil from "@/util/layoututil";
|
import * as layoututil from "@/util/layoututil";
|
||||||
import * as util from "@/util/util";
|
import * as util from "@/util/util";
|
||||||
|
import * as csstree from "css-tree";
|
||||||
import { getLayoutStateAtomForTab, globalLayoutTransformsMap } from "frontend/layout/lib/layoutAtom";
|
import { getLayoutStateAtomForTab, globalLayoutTransformsMap } from "frontend/layout/lib/layoutAtom";
|
||||||
import * as jotai from "jotai";
|
import * as jotai from "jotai";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
@ -15,6 +16,8 @@ import { DndProvider } from "react-dnd";
|
|||||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||||
import { CenteredDiv } from "./element/quickelems";
|
import { CenteredDiv } from "./element/quickelems";
|
||||||
|
|
||||||
|
import { useWaveObjectValue } from "@/app/store/wos";
|
||||||
|
import { getWebServerEndpoint } from "@/util/endpoints";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import Color from "color";
|
import Color from "color";
|
||||||
import "overlayscrollbars/overlayscrollbars.css";
|
import "overlayscrollbars/overlayscrollbars.css";
|
||||||
@ -208,7 +211,6 @@ function AppSettingsUpdater() {
|
|||||||
const opacity = util.boundNumber(settings?.window?.opacity ?? 0.8, 0, 1);
|
const opacity = util.boundNumber(settings?.window?.opacity ?? 0.8, 0, 1);
|
||||||
let baseBgColor = settings?.window?.bgcolor;
|
let baseBgColor = settings?.window?.bgcolor;
|
||||||
console.log("window settings", settings.window);
|
console.log("window settings", settings.window);
|
||||||
|
|
||||||
if (isTransparentOrBlur) {
|
if (isTransparentOrBlur) {
|
||||||
document.body.classList.add("is-transparent");
|
document.body.classList.add("is-transparent");
|
||||||
const rootStyles = getComputedStyle(document.documentElement);
|
const rootStyles = getComputedStyle(document.documentElement);
|
||||||
@ -226,6 +228,76 @@ function AppSettingsUpdater() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function encodeFileURL(file: string) {
|
||||||
|
const webEndpoint = getWebServerEndpoint();
|
||||||
|
return webEndpoint + `/wave/stream-file?path=${encodeURIComponent(file)}&no404=1`;
|
||||||
|
}
|
||||||
|
|
||||||
|
(window as any).csstree = csstree;
|
||||||
|
|
||||||
|
function processBackgroundUrls(cssText: string): string {
|
||||||
|
if (util.isBlank(cssText)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
cssText = cssText.trim();
|
||||||
|
if (cssText.endsWith(";")) {
|
||||||
|
cssText = cssText.slice(0, -1);
|
||||||
|
}
|
||||||
|
const attrRe = /^background(-image):\s*/;
|
||||||
|
cssText = cssText.replace(attrRe, "");
|
||||||
|
const ast = csstree.parse("background: " + cssText, {
|
||||||
|
context: "declaration",
|
||||||
|
});
|
||||||
|
let hasJSUrl = false;
|
||||||
|
csstree.walk(ast, {
|
||||||
|
visit: "Url",
|
||||||
|
enter(node) {
|
||||||
|
const originalUrl = node.value.trim();
|
||||||
|
if (originalUrl.startsWith("javascript:")) {
|
||||||
|
hasJSUrl = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newUrl = encodeFileURL(originalUrl);
|
||||||
|
node.value = newUrl;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (hasJSUrl) {
|
||||||
|
console.log("invalid background, contains a 'javascript' protocol url which is not allowed");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const rtnStyle = csstree.generate(ast);
|
||||||
|
if (rtnStyle == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return rtnStyle.replace(/^background:\s*/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
const backgroundAttr = "url(/Users/mike/Downloads/wave-logo_appicon.png) repeat-x fixed";
|
||||||
|
|
||||||
|
function AppBackground() {
|
||||||
|
const tabId = jotai.useAtomValue(atoms.activeTabId);
|
||||||
|
const [tabData] = useWaveObjectValue<Tab>(WOS.makeORef("tab", tabId));
|
||||||
|
const bgAttr = tabData?.meta?.bg;
|
||||||
|
const style: React.CSSProperties = {};
|
||||||
|
if (!util.isBlank(bgAttr)) {
|
||||||
|
try {
|
||||||
|
const processedBg = processBackgroundUrls(bgAttr);
|
||||||
|
if (!util.isBlank(processedBg)) {
|
||||||
|
const opacity = util.boundNumber(tabData?.meta?.["bg:opacity"], 0, 1) ?? 0.5;
|
||||||
|
style.opacity = opacity;
|
||||||
|
style.background = processedBg;
|
||||||
|
const blendMode = tabData?.meta?.["bg:blendmode"];
|
||||||
|
if (!util.isBlank(blendMode)) {
|
||||||
|
style.backgroundBlendMode = blendMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("error processing background", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return <div className="app-background" style={style} />;
|
||||||
|
}
|
||||||
|
|
||||||
const AppInner = () => {
|
const AppInner = () => {
|
||||||
const client = jotai.useAtomValue(atoms.client);
|
const client = jotai.useAtomValue(atoms.client);
|
||||||
const windowData = jotai.useAtomValue(atoms.waveWindow);
|
const windowData = jotai.useAtomValue(atoms.waveWindow);
|
||||||
@ -233,6 +305,7 @@ const AppInner = () => {
|
|||||||
if (client == null || windowData == null) {
|
if (client == null || windowData == null) {
|
||||||
return (
|
return (
|
||||||
<div className="mainapp">
|
<div className="mainapp">
|
||||||
|
<AppBackground />
|
||||||
<CenteredDiv>invalid configuration, client or window was not loaded</CenteredDiv>
|
<CenteredDiv>invalid configuration, client or window was not loaded</CenteredDiv>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -277,6 +350,7 @@ const AppInner = () => {
|
|||||||
const isFullScreen = jotai.useAtomValue(atoms.isFullScreen);
|
const isFullScreen = jotai.useAtomValue(atoms.isFullScreen);
|
||||||
return (
|
return (
|
||||||
<div className={clsx("mainapp", PLATFORM, { fullscreen: isFullScreen })} onContextMenu={handleContextMenu}>
|
<div className={clsx("mainapp", PLATFORM, { fullscreen: isFullScreen })} onContextMenu={handleContextMenu}>
|
||||||
|
<AppBackground />
|
||||||
<AppSettingsUpdater />
|
<AppSettingsUpdater />
|
||||||
<DndProvider backend={HTML5Backend}>
|
<DndProvider backend={HTML5Backend}>
|
||||||
<Workspace />
|
<Workspace />
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: rgba(255, 255, 255, 0.05);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.animate {
|
&.animate {
|
||||||
|
@ -7,7 +7,7 @@ import * as services from "@/store/services";
|
|||||||
import * as WOS from "@/store/wos";
|
import * as WOS from "@/store/wos";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { CenteredDiv, CenteredLoadingDiv } from "@/element/quickelems";
|
import { CenteredDiv } from "@/element/quickelems";
|
||||||
import { TileLayout } from "frontend/layout/index";
|
import { TileLayout } from "frontend/layout/index";
|
||||||
import { getLayoutStateAtomForTab } from "frontend/layout/lib/layoutAtom";
|
import { getLayoutStateAtomForTab } from "frontend/layout/lib/layoutAtom";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
@ -56,7 +56,11 @@ const TabContent = React.memo(({ tabId }: { tabId: string }) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (tabLoading) {
|
if (tabLoading) {
|
||||||
return <CenteredLoadingDiv />;
|
return (
|
||||||
|
<div className="tabcontent">
|
||||||
|
<CenteredDiv>Tab Loading</CenteredDiv>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tabData) {
|
if (!tabData) {
|
||||||
|
@ -35,6 +35,9 @@ function base64ToArray(b64: string): Uint8Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function boundNumber(num: number, min: number, max: number): number {
|
function boundNumber(num: number, min: number, max: number): number {
|
||||||
|
if (num == null || typeof num != "number" || isNaN(num)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return Math.min(Math.max(num, min), max);
|
return Math.min(Math.max(num, min), max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
"@storybook/react": "^8.2.6",
|
"@storybook/react": "^8.2.6",
|
||||||
"@storybook/react-vite": "^8.2.6",
|
"@storybook/react-vite": "^8.2.6",
|
||||||
"@storybook/test": "^8.2.6",
|
"@storybook/test": "^8.2.6",
|
||||||
|
"@types/css-tree": "^2",
|
||||||
"@types/electron": "^1.6.10",
|
"@types/electron": "^1.6.10",
|
||||||
"@types/node": "^20.14.12",
|
"@types/node": "^20.14.12",
|
||||||
"@types/papaparse": "^5",
|
"@types/papaparse": "^5",
|
||||||
@ -84,6 +85,7 @@
|
|||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"color": "^4.2.3",
|
"color": "^4.2.3",
|
||||||
|
"css-tree": "^2.3.1",
|
||||||
"dayjs": "^1.11.12",
|
"dayjs": "^1.11.12",
|
||||||
"electron-updater": "6.3.1",
|
"electron-updater": "6.3.1",
|
||||||
"html-to-image": "^1.11.11",
|
"html-to-image": "^1.11.11",
|
||||||
|
@ -307,7 +307,7 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta map[str
|
|||||||
if bc.ControllerType == BlockController_Shell {
|
if bc.ControllerType == BlockController_Shell {
|
||||||
cmdOpts.Interactive = true
|
cmdOpts.Interactive = true
|
||||||
cmdOpts.Login = true
|
cmdOpts.Login = true
|
||||||
cmdOpts.Cwd, _ = blockMeta["cwd"].(string)
|
cmdOpts.Cwd, _ = blockMeta["cmd:cwd"].(string)
|
||||||
if cmdOpts.Cwd != "" {
|
if cmdOpts.Cwd != "" {
|
||||||
cmdOpts.Cwd = wavebase.ExpandHomeDir(cmdOpts.Cwd)
|
cmdOpts.Cwd = wavebase.ExpandHomeDir(cmdOpts.Cwd)
|
||||||
}
|
}
|
||||||
@ -317,8 +317,8 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta map[str
|
|||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("missing cmd in block meta")
|
return fmt.Errorf("missing cmd in block meta")
|
||||||
}
|
}
|
||||||
if _, ok := blockMeta["cwd"].(string); ok {
|
if _, ok := blockMeta["cmd:cwd"].(string); ok {
|
||||||
cmdOpts.Cwd = blockMeta["cwd"].(string)
|
cmdOpts.Cwd = blockMeta["cmd:cwd"].(string)
|
||||||
if cmdOpts.Cwd != "" {
|
if cmdOpts.Cwd != "" {
|
||||||
cmdOpts.Cwd = wavebase.ExpandHomeDir(cmdOpts.Cwd)
|
cmdOpts.Cwd = wavebase.ExpandHomeDir(cmdOpts.Cwd)
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,8 @@ import (
|
|||||||
|
|
||||||
type WebFnType = func(http.ResponseWriter, *http.Request)
|
type WebFnType = func(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
const TransparentGif64 = "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||||||
|
|
||||||
// Header constants
|
// Header constants
|
||||||
const (
|
const (
|
||||||
CacheControlHeaderKey = "Cache-Control"
|
CacheControlHeaderKey = "Cache-Control"
|
||||||
@ -55,6 +57,45 @@ type WebFnOpts struct {
|
|||||||
JsonErrors bool
|
JsonErrors bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func copyHeaders(dst, src http.Header) {
|
||||||
|
for key, values := range src {
|
||||||
|
for _, value := range values {
|
||||||
|
dst.Add(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type notFoundBlockingResponseWriter struct {
|
||||||
|
w http.ResponseWriter
|
||||||
|
status int
|
||||||
|
headers http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *notFoundBlockingResponseWriter) Header() http.Header {
|
||||||
|
return rw.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *notFoundBlockingResponseWriter) WriteHeader(status int) {
|
||||||
|
if status == http.StatusNotFound {
|
||||||
|
rw.status = status
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rw.status = status
|
||||||
|
copyHeaders(rw.w.Header(), rw.headers)
|
||||||
|
rw.w.WriteHeader(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *notFoundBlockingResponseWriter) Write(b []byte) (int, error) {
|
||||||
|
if rw.status == http.StatusNotFound {
|
||||||
|
// Block the write if it's a 404
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
if rw.status == 0 {
|
||||||
|
rw.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
return rw.w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
func handleService(w http.ResponseWriter, r *http.Request) {
|
func handleService(w http.ResponseWriter, r *http.Request) {
|
||||||
bodyData, err := io.ReadAll(r.Body)
|
bodyData, err := io.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -160,14 +201,36 @@ func handleWaveFile(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func serveTransparentGIF(w http.ResponseWriter) {
|
||||||
|
gifBytes, _ := base64.StdEncoding.DecodeString(TransparentGif64)
|
||||||
|
w.Header().Set("Content-Type", "image/gif")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(gifBytes)
|
||||||
|
}
|
||||||
|
|
||||||
func handleStreamFile(w http.ResponseWriter, r *http.Request) {
|
func handleStreamFile(w http.ResponseWriter, r *http.Request) {
|
||||||
fileName := r.URL.Query().Get("path")
|
fileName := r.URL.Query().Get("path")
|
||||||
if fileName == "" {
|
if fileName == "" {
|
||||||
http.Error(w, "path is required", http.StatusBadRequest)
|
http.Error(w, "path is required", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fileName = wavebase.ExpandHomeDir(fileName)
|
no404 := r.URL.Query().Get("no404")
|
||||||
http.ServeFile(w, r, fileName)
|
log.Printf("got no404: %q\n", no404)
|
||||||
|
if no404 != "" {
|
||||||
|
log.Printf("streaming file w/no404: %q\n", fileName)
|
||||||
|
// use the custom response writer
|
||||||
|
rw := ¬FoundBlockingResponseWriter{w: w, headers: http.Header{}}
|
||||||
|
// Serve the file using http.ServeFile
|
||||||
|
http.ServeFile(rw, r, fileName)
|
||||||
|
// if the file was not found, serve the transparent GIF
|
||||||
|
log.Printf("got streamfile status: %d\n", rw.status)
|
||||||
|
if rw.status == http.StatusNotFound {
|
||||||
|
serveTransparentGIF(w)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileName = wavebase.ExpandHomeDir(fileName)
|
||||||
|
http.ServeFile(w, r, fileName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WebFnWrap(opts WebFnOpts, fn WebFnType) WebFnType {
|
func WebFnWrap(opts WebFnOpts, fn WebFnType) WebFnType {
|
||||||
|
@ -28,10 +28,38 @@ const (
|
|||||||
MetaKey_CmdRunOnStart = "cmd:runonstart"
|
MetaKey_CmdRunOnStart = "cmd:runonstart"
|
||||||
MetaKey_CmdClearOnStart = "cmd:clearonstart"
|
MetaKey_CmdClearOnStart = "cmd:clearonstart"
|
||||||
MetaKey_CmdClearOnRestart = "cmd:clearonrestart"
|
MetaKey_CmdClearOnRestart = "cmd:clearonrestart"
|
||||||
MetaKey_CmdEnv = "env"
|
MetaKey_CmdEnv = "cmd:env"
|
||||||
MetaKey_CmdCwd = "cwd"
|
MetaKey_CmdCwd = "cmd:cwd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type MetaType struct {
|
||||||
|
View string `json:"view,omitempty"`
|
||||||
|
Controller string `json:"controller,omitempty"`
|
||||||
|
Title string `json:"title,omitempty"`
|
||||||
|
File string `json:"file,omitempty"`
|
||||||
|
Url string `json:"url,omitempty"`
|
||||||
|
|
||||||
|
Icon string `json:"icon,omitempty"`
|
||||||
|
IconColor string `json:"icon:color,omitempty"`
|
||||||
|
|
||||||
|
Frame bool `json:"frame,omitempty"`
|
||||||
|
FrameBorderColor string `json:"frame:bordercolor,omitempty"`
|
||||||
|
FrameBorderColor_Focused string `json:"frame:bordercolor:focused,omitempty"`
|
||||||
|
|
||||||
|
Cmd string `json:"cmd,omitempty"`
|
||||||
|
CmdInteractive bool `json:"cmd:interactive,omitempty"`
|
||||||
|
CmdLogin bool `json:"cmd:login,omitempty"`
|
||||||
|
CmdRunOnStart bool `json:"cmd:runonstart,omitempty"`
|
||||||
|
CmdClearOnStart bool `json:"cmd:clearonstart,omitempty"`
|
||||||
|
CmdClearOnRestart bool `json:"cmd:clearonrestart,omitempty"`
|
||||||
|
CmdEnv map[string]string `json:"cmd:env,omitempty"`
|
||||||
|
CmdCwd string `json:"cmd:cwd,omitempty"`
|
||||||
|
|
||||||
|
Bg string `json:"bg,omitempty"`
|
||||||
|
BgOpacity float64 `json:"bg:opacity,omitempty"`
|
||||||
|
BgBlendMode string `json:"bg:blendmode,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type UIContext struct {
|
type UIContext struct {
|
||||||
WindowId string `json:"windowid"`
|
WindowId string `json:"windowid"`
|
||||||
ActiveTabId string `json:"activetabid"`
|
ActiveTabId string `json:"activetabid"`
|
||||||
|
28
yarn.lock
28
yarn.lock
@ -3589,6 +3589,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/css-tree@npm:^2":
|
||||||
|
version: 2.3.8
|
||||||
|
resolution: "@types/css-tree@npm:2.3.8"
|
||||||
|
checksum: 10c0/fafa7ad516b64481a031aceb3c30762074e1e0bfd67e0f0655e46b8c1b7b3c39660f8285811ca6aac11229ef477c65ca61ee118d2f9264145d5db8fe26f1a721
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/debug@npm:^4.0.0, @types/debug@npm:^4.1.6":
|
"@types/debug@npm:^4.0.0, @types/debug@npm:^4.1.6":
|
||||||
version: 4.1.12
|
version: 4.1.12
|
||||||
resolution: "@types/debug@npm:4.1.12"
|
resolution: "@types/debug@npm:4.1.12"
|
||||||
@ -5510,6 +5517,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"css-tree@npm:^2.3.1":
|
||||||
|
version: 2.3.1
|
||||||
|
resolution: "css-tree@npm:2.3.1"
|
||||||
|
dependencies:
|
||||||
|
mdn-data: "npm:2.0.30"
|
||||||
|
source-map-js: "npm:^1.0.1"
|
||||||
|
checksum: 10c0/6f8c1a11d5e9b14bf02d10717fc0351b66ba12594166f65abfbd8eb8b5b490dd367f5c7721db241a3c792d935fc6751fbc09f7e1598d421477ad9fadc30f4f24
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"css.escape@npm:^1.5.1":
|
"css.escape@npm:^1.5.1":
|
||||||
version: 1.5.1
|
version: 1.5.1
|
||||||
resolution: "css.escape@npm:1.5.1"
|
resolution: "css.escape@npm:1.5.1"
|
||||||
@ -9080,6 +9097,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"mdn-data@npm:2.0.30":
|
||||||
|
version: 2.0.30
|
||||||
|
resolution: "mdn-data@npm:2.0.30"
|
||||||
|
checksum: 10c0/a2c472ea16cee3911ae742593715aa4c634eb3d4b9f1e6ada0902aa90df13dcbb7285d19435f3ff213ebaa3b2e0c0265c1eb0e3fb278fda7f8919f046a410cd9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"media-typer@npm:0.3.0":
|
"media-typer@npm:0.3.0":
|
||||||
version: 0.3.0
|
version: 0.3.0
|
||||||
resolution: "media-typer@npm:0.3.0"
|
resolution: "media-typer@npm:0.3.0"
|
||||||
@ -11434,7 +11458,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"source-map-js@npm:^1.2.0":
|
"source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.0":
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
resolution: "source-map-js@npm:1.2.0"
|
resolution: "source-map-js@npm:1.2.0"
|
||||||
checksum: 10c0/7e5f896ac10a3a50fe2898e5009c58ff0dc102dcb056ed27a354623a0ece8954d4b2649e1a1b2b52ef2e161d26f8859c7710350930751640e71e374fe2d321a4
|
checksum: 10c0/7e5f896ac10a3a50fe2898e5009c58ff0dc102dcb056ed27a354623a0ece8954d4b2649e1a1b2b52ef2e161d26f8859c7710350930751640e71e374fe2d321a4
|
||||||
@ -11818,6 +11842,7 @@ __metadata:
|
|||||||
"@table-nav/react": "npm:^0.0.7"
|
"@table-nav/react": "npm:^0.0.7"
|
||||||
"@tanstack/react-table": "npm:^8.19.3"
|
"@tanstack/react-table": "npm:^8.19.3"
|
||||||
"@types/color": "npm:^3.0.6"
|
"@types/color": "npm:^3.0.6"
|
||||||
|
"@types/css-tree": "npm:^2"
|
||||||
"@types/electron": "npm:^1.6.10"
|
"@types/electron": "npm:^1.6.10"
|
||||||
"@types/node": "npm:^20.14.12"
|
"@types/node": "npm:^20.14.12"
|
||||||
"@types/papaparse": "npm:^5"
|
"@types/papaparse": "npm:^5"
|
||||||
@ -11835,6 +11860,7 @@ __metadata:
|
|||||||
base64-js: "npm:^1.5.1"
|
base64-js: "npm:^1.5.1"
|
||||||
clsx: "npm:^2.1.1"
|
clsx: "npm:^2.1.1"
|
||||||
color: "npm:^4.2.3"
|
color: "npm:^4.2.3"
|
||||||
|
css-tree: "npm:^2.3.1"
|
||||||
dayjs: "npm:^1.11.12"
|
dayjs: "npm:^1.11.12"
|
||||||
electron: "npm:^31.3.0"
|
electron: "npm:^31.3.0"
|
||||||
electron-builder: "npm:^24.13.3"
|
electron-builder: "npm:^24.13.3"
|
||||||
|
Loading…
Reference in New Issue
Block a user