diff --git a/package-lock.json b/package-lock.json index d523fba..99ba185 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "license": "MIT", "dependencies": { "date-fns": "^4.1.0", + "lucide": "^0.469.0", + "lucide-react": "^0.469.0", "react": "^19.0.0", "react-dom": "^19.0.0" }, @@ -1718,6 +1720,19 @@ "dev": true, "peer": true }, + "node_modules/lucide": { + "version": "0.469.0", + "resolved": "https://registry.npmjs.org/lucide/-/lucide-0.469.0.tgz", + "integrity": "sha512-ZmMtOekJ6g1UFqTvV9aWbkk+u/QxpfDpgUrbNj8jJEK+CtBdHVQ4akCA0TcTyeOvnL8RWo271UzFTimBRT0gKw==" + }, + "node_modules/lucide-react": { + "version": "0.469.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.469.0.tgz", + "integrity": "sha512-28vvUnnKQ/dBwiCQtwJw7QauYnE7yd2Cyp4tTTJpvglX4EMpbflcdBgrgToX2j71B3YvugK/NH3BGUk+E/p/Fw==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", diff --git a/package.json b/package.json index d530ed6..a6902de 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ }, "dependencies": { "date-fns": "^4.1.0", + "lucide-react": "^0.469.0", "react": "^19.0.0", "react-dom": "^19.0.0" } diff --git a/src/main/components/App.tsx b/src/main/components/App.tsx new file mode 100644 index 0000000..7c54b32 --- /dev/null +++ b/src/main/components/App.tsx @@ -0,0 +1,191 @@ +import { useEffect, useState } from "react"; +import { useTableContext } from "../views/TableView"; +import Table from "./Table"; +import TopBar from "./TopBar"; +import { format, addDays, startOfWeek, subDays } from "date-fns"; + +export interface Project { + id: number; + name: string; + tasks: Task[]; +} + +export interface Task { + id: number; + name: string; + date: Date; + parentProjectId: number; +} + +const getWeekDates = (date: Date) => { + const startDate = startOfWeek(date, { weekStartsOn: 1 }); + const dates = Array.from({ length: 7 }, (_, i) => { + return format(addDays(startDate, i), "yyyy-MM-dd"); + }); + return dates; +}; + +const App: React.FC = () => { + const tableContext = useTableContext(); + + const [dates, setDates] = useState(getWeekDates(new Date())); + + const [data, setData] = useState(() => tableContext!.loadData()); + + const [projects, setProjects] = useState( + () => tableContext!.loadData().projects as Project[] + ); + const [nextProjectId, setNextProjectId] = useState( + () => tableContext!.loadData().nextProjectId as number + ); + const [nextTaskId, setNextTaskId] = useState( + () => tableContext!.loadData().nextProjectId as number + ); + + const incrementDates = () => { + setDates((prevDates) => { + const newDates = getWeekDates(addDays(new Date(prevDates[0]), 8)); + return newDates; + }); + }; + + const decrementDates = () => { + setDates((prevDates) => { + const newDates = getWeekDates(subDays(new Date(prevDates[0]), 8)); + return newDates; + }); + }; + + const setDatesToThisWeek = () => { + setDates((prevDates) => { + const newDates = getWeekDates(new Date()); + return newDates; + }); + }; + + const handleProjectNameChange = (id: number, newName: string) => { + setProjects((prevProjects) => { + const newProjects = prevProjects.map((project) => + project.id === id ? { ...project, name: newName } : project + ); + saveSpecificData("projects", newProjects); + return newProjects; + }); + }; + + const removeProject = (id: number) => { + setProjects((prevProjects) => { + const newProjects = prevProjects.filter( + (project) => project.id !== id + ); + saveSpecificData("projects", newProjects); + return newProjects; + }); + }; + + const removeTask = (id: number) => { + setProjects((prevProjects) => { + const newProjects = prevProjects.map((project) => { + const newTasks = project.tasks.filter((task) => task.id !== id); + return { ...project, tasks: newTasks }; + }); + saveSpecificData("projects", newProjects); + return newProjects; + }); + }; + + const createNewProject = ( + newName: string, + newProjectInputRef: React.RefObject + ) => { + if (!newName.trim()) return; + setProjects((prevProjects) => { + const newProjects = [ + ...prevProjects, + { id: nextProjectId, name: newName, tasks: [] }, + ]; + + saveSpecificData("projects", newProjects); + + return newProjects; + }); + + setNextProjectId((prevId) => { + const newId = prevId + 1; + saveSpecificData("nextProjectId", newId); + return newId; + }); + + setTimeout(() => { + if (newProjectInputRef.current) { + newProjectInputRef.current.focus(); + } + }, 0); + }; + + const addTaskToProject = (project: Project, date: string) => { + const newTask = { + id: nextTaskId, + name: "", + date: new Date(date), + parentProjectId: project.id, + }; + + setProjects((prevProjects) => { + const newProjects = prevProjects.map((p) => { + if (p.id === project.id) { + return { ...p, tasks: [...p.tasks, newTask] }; + } + return p; + }); + + saveSpecificData("projects", newProjects); + + return newProjects; + }); + + setNextTaskId((prevId) => { + const newId = prevId + 1; + saveSpecificData("nextTaskId", newId); + return newId; + }); + }; + + const saveSpecificData = (key: string, value: any): void => { + setData((prevData: any) => { + const newData = { ...prevData, [key]: value }; + return newData; + }); + }; + + useEffect(() => { + if (tableContext) { + tableContext.saveData(data); + } else { + console.log("not exist"); + } + }, [projects, nextProjectId]); + + return ( +
+ + + + ); +}; + +export default App; diff --git a/src/main/components/Table.tsx b/src/main/components/Table.tsx new file mode 100644 index 0000000..ec41561 --- /dev/null +++ b/src/main/components/Table.tsx @@ -0,0 +1,158 @@ +import { format } from "date-fns"; +import { useEffect, useRef } from "react"; +import * as React from "react"; +import { Project } from "./App"; +import TaskCell from "./TaskCell"; + +interface TableProps { + projects: Project[]; + nextProjectId: number; + handleProjectNameChange: (id: number, newName: string) => void; + createNewProject: ( + newName: string, + newProjectInputRef: React.RefObject + ) => void; + dates: string[]; + addTaskToProject: (project: Project, date: string) => void; + removeProject: (id: number) => void; + removeTask: (id: number) => void; + nextTaskId: number; +} + +const Table: React.FC = ({ + projects, + nextProjectId, + handleProjectNameChange, + createNewProject, + dates, + addTaskToProject, + removeProject, + removeTask, + nextTaskId +}) => { + const containerRef = useRef(null); + const newProjectInputRef = useRef(null); + const newTaskInputRef = useRef(null); + + useEffect(() => { + const handleResize = () => { + const containerWidth = containerRef.current?.clientWidth; + if (containerWidth) { + document.documentElement.style.setProperty( + "--taskcell-enclosure-width", + `${(containerWidth - 128) / 7}px` + ); + } + }; + + const resizeObserver = new ResizeObserver(handleResize); + if (containerRef.current) { + resizeObserver.observe(containerRef.current); + } + + // Initial call to set the variable + handleResize(); + + return () => { + if (containerRef.current) { + resizeObserver.unobserve(containerRef.current); + } + }; + }, [containerRef]); + + return ( +
+
+
+ + + + {dates.map((date) => ( + + ))} + + + + {projects.map((project) => ( + + + {dates.map((date, index) => ( + + ))} + + ))} + +
Projects{`${format( + date, + "EEEE" + )} - ${format(date, "yyyy-MM-dd")}`}
+ {if (e.target.value === "") removeProject(project.id)}} + onChange={(e) => + handleProjectNameChange( + project.id, + e.target.value + ) + } + /> + { + if (e.target === e.currentTarget) { + addTaskToProject(project, date); + setTimeout(() => { + if (newTaskInputRef.current) { + newTaskInputRef.current.focus(); + } + }, 0); + } + }} + > + {project.tasks + .filter( + (task) => + new Date( + task.date + ).toISOString() === + new Date(date).toISOString() + ) + .map((task) => ( + + ))} +
+
+
+ { + createNewProject(e.target.value, newProjectInputRef); + e.target.value = ""; + }} + /> +
+ + ); +}; + +export default Table; diff --git a/src/main/components/TaskCell.tsx b/src/main/components/TaskCell.tsx new file mode 100644 index 0000000..a0d32f3 --- /dev/null +++ b/src/main/components/TaskCell.tsx @@ -0,0 +1,61 @@ +import { Task } from "./App"; + +const transColor = (color: string, percent: number): string => { + const num = parseInt(color.replace("#", ""), 16); + const amt = Math.round(2.55 * percent); + const R = (num >> 16) + amt; + const B = ((num >> 8) & 0x00ff) + amt; + const G = (num & 0x0000ff) + amt; + return ( + "#" + + ( + 0x1000000 + + (R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 + + (B < 255 ? (B < 1 ? 0 : B) : 255) * 0x100 + + (G < 255 ? (G < 1 ? 0 : G) : 255) + ) + .toString(16) + .slice(1) + ); +}; + +interface TaskCellProps { + task: Task; + removeTask: (id: number) => void; + inputRef: React.RefObject | null; +} + +const TaskCell: React.FC = ({task, removeTask, inputRef}) => { + const color = "#35CC70"; + + document.documentElement.style.setProperty("--task-color", color); + document.documentElement.style.setProperty( + "--task-background", + `${color}33` + ); + document.documentElement.style.setProperty( + "--dark-task-text-color", + transColor(color, -40) + ); + document.documentElement.style.setProperty( + "--light-task-text-color", + transColor(color, 25) + ); + + return ( +
+
Apush
+
+ { + if (e.target.value === "") removeTask(task.id); + }} + ref={inputRef} + > +
+
+ ); +}; + +export default TaskCell; diff --git a/src/main/components/TopBar.tsx b/src/main/components/TopBar.tsx new file mode 100644 index 0000000..ce6758a --- /dev/null +++ b/src/main/components/TopBar.tsx @@ -0,0 +1,23 @@ +import { ChevronLeft, ChevronRight } from "lucide-react"; + +interface TopBarProps { + incrementDates: () => void; + decrementDates: () => void; + setDatesToThisWeek: () => void; +} + +const TopBar: React.FC = ({ incrementDates, decrementDates, setDatesToThisWeek }) => { + return ( +
+
+ +
+ {new Date().toDateString()} +
+ +
+
+ ); +}; + +export default TopBar; diff --git a/src/main/react/Table.tsx b/src/main/react/Table.tsx deleted file mode 100644 index 5b611c0..0000000 --- a/src/main/react/Table.tsx +++ /dev/null @@ -1,195 +0,0 @@ -// import "styles.css"; -import { format, addDays, startOfWeek } from "date-fns"; -import { useEffect, useRef, useState } from "react"; -import * as React from "react"; -import { useTableContext } from "../views/TableView"; - -interface Project { - id: number; - name: string; - tasks: Task[]; -} - -interface Task { - id: number; - name: string; - date: Date; - parentProjectId: number; -} - -const getInitialDates = () => { - const startDate = startOfWeek(new Date(), { weekStartsOn: 1 }); - const dates = Array.from({ length: 7 }, (_, i) => { - return format(addDays(startDate, i), "yyyy-MM-dd"); - }); - return dates; -}; - -const Table: React.FC = () => { - const tableContext = useTableContext(); - - const [data, setData] = useState(() => tableContext!.loadData()); - - const [dates, setDates] = useState(getInitialDates); - - const [projects, setProjects] = useState( - () => tableContext!.loadData().projects as Project[] - ); - const [nextProjectId, setNextProjectId] = useState( - () => tableContext!.loadData().nextProjectId as number - ); - - const containerRef = useRef(null); - const newProjectInputRef = useRef(null); - - const saveSpecificData = (key: string, value: any): void => { - setData((prevData: any) => { - const newData = { ...prevData, [key]: value }; - return newData; - }); - }; - - useEffect(() => { - const handleResize = () => { - const containerWidth = containerRef.current?.clientWidth; - if (containerWidth) { - document.documentElement.style.setProperty( - "--taskcell-enclosure-width", - `${(containerWidth - 128) / 7}px` - ); - } - }; - - const resizeObserver = new ResizeObserver(handleResize); - if (containerRef.current) { - resizeObserver.observe(containerRef.current); - } - - // Initial call to set the variable - handleResize(); - - return () => { - if (containerRef.current) { - resizeObserver.unobserve(containerRef.current); - } - }; - }, [containerRef]); - - const handleProjectNameChange = (id: number, newName: string) => { - if (!newName.trim()) { - setProjects((prevProjects) => { - const newProjects = prevProjects.filter( - (project) => project.id !== id - ); - saveSpecificData("projects", newProjects); - return newProjects; - }); - } else { - setProjects((prevProjects) => { - const newProjects = prevProjects.map((project) => - project.id === id ? { ...project, name: newName } : project - ); - saveSpecificData("projects", newProjects); - return newProjects; - }); - } - }; - - const createNewProject = (newName: string) => { - if (!newName.trim()) return; - setProjects((prevProjects) => { - const newProjects = [ - ...prevProjects, - { id: nextProjectId, name: newName, tasks: [] }, - ]; - - saveSpecificData("projects", newProjects); - - return newProjects; - }); - - setNextProjectId((prevId) => { - const newId = prevId + 1; - saveSpecificData("nextProjectId", newId); - return newId; - }); - - setTimeout(() => { - if (newProjectInputRef.current) { - newProjectInputRef.current.focus(); - } - }, 0); - }; - - useEffect(() => { - if (tableContext) { - tableContext.saveData(data); - } else { - console.log("not exist"); - } - }, [projects, nextProjectId]); - - return ( -
-
- - - - - {dates.map((date) => ( - - ))} - - - - {projects.map((project) => ( - - - {dates.map((date, index) => ( - - ))} - - ))} - -
Projects{`${format( - date, - "EEEE" - )} - ${format(date, "yyyy-MM-dd")}`}
- - handleProjectNameChange( - project.id, - e.target.value - ) - } - /> -
-
-
- { - createNewProject(e.target.value); - e.target.value = ""; - }} - /> -
-
- ); -}; - -export default Table; diff --git a/src/main/views/TableView.tsx b/src/main/views/TableView.tsx index f03c00d..4005e7a 100644 --- a/src/main/views/TableView.tsx +++ b/src/main/views/TableView.tsx @@ -1,7 +1,7 @@ import { createContext, useContext } from 'react'; import { ItemView, WorkspaceLeaf, App } from 'obsidian'; import { Root, createRoot } from 'react-dom/client'; -import Table from 'src/main/react/Table'; +import AppComponent from 'src/main/components/App'; export interface AppContext { app: App; @@ -45,7 +45,7 @@ export class TableView extends ItemView { this.root = createRoot(this.containerEl.children[1]); this.root.render( - + ); } diff --git a/styles.css b/styles.css index feda2f3..34dcbe5 100644 --- a/styles.css +++ b/styles.css @@ -1,4 +1,102 @@ -/* styles.css */ +/* App */ +.app-wrapper { + display: flex; + flex-direction: column; +} + +/* Top Bar */ +.top-bar { + margin-bottom: 8px; + display: flex; + justify-content: center; + align-items: center; +} + +.date-increment-container { + display: flex; + justify-content: center; + align-items: center; + width: min-content; +} + +.date-display { + width: max-content; + font-weight: bold !important; + font-size: large !important; + padding: 0px 24px; + height: 36px; + display: flex; + align-items: center; + background-color: var(--background-secondary); + border-left: var(--border-width) solid var(--background-modifier-border); + border-top: var(--border-width) solid var(--background-modifier-border); + border-bottom: var(--border-width) solid var(--background-modifier-border); +} + +.date-display:hover { + background-color: var(--background-secondary-alt); +} + +.date-increment { + height: 36px; + width: 36px; + padding: 6px; + border-left: var(--border-width) solid var(--background-modifier-border); + border-top: var(--border-width) solid var(--background-modifier-border); + border-bottom: var(--border-width) solid var(--background-modifier-border); + background-color: var(--background-secondary); +} + +.date-increment:hover { + background-color: var(--background-secondary-alt); +} + +.left-increment { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} + +.right-increment { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + border-right: var(--border-width) solid var(--background-modifier-border); +} + +/* Task Cell */ + +.task-cell { + background: var(--task-background); + opacity: 0.8; + border-radius: 8px; + padding: 4px; + display: flex; + flex-direction: column; + margin: 4px; +} + +.task-cell-header { + background: var(--task-background); + border-radius: 4px; + margin-bottom: 4px; + color: var(--light-task-text-color); + padding-left: 4px; +} + +.task-cell-content input { + background: transparent; + border: none; + color: var(--light-task-text-color); + margin-bottom: 4px; + font-size: 14px; + width: 100%; +} + +.task-cell-content input::placeholder { + color: var(--light-task-text-color); + opacity: 0.5; +} + +/* Table */ .table-container { border: var(--border-width) solid var(--background-modifier-border); border-top-left-radius: 8px; @@ -38,9 +136,10 @@ width: 128px; } .table td { - padding: 12px 16px; + padding: 8px; font-size: small; - height: 68px; + height: 80px; + /* height: 128px; */ border-top: var(--border-width) solid var(--background-modifier-border); } .taskcell-enclosure { @@ -58,4 +157,4 @@ border-left: var(--border-width) solid var(--background-modifier-border); width: var(--taskcell-enclosure-width); } -/*# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3R5bGVzLmNzcyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiLyogRGVmaW5lIGEgY3VzdG9tIGJvcmRlciBjb2xvciAqL1xyXG4udGFibGUtY29udGFpbmVyIHtcclxuICBib3JkZXI6IHZhcigtLWJvcmRlci13aWR0aCkgc29saWQgdmFyKC0tYmFja2dyb3VuZC1tb2RpZmllci1ib3JkZXIpOyAvKiBDdXN0b20gYm9yZGVyIGNvbG9yICovXHJcbiAgYm9yZGVyLXJhZGl1czogOHB4OyAvKiBSb3VuZGVkIGNvcm5lcnMgKi9cclxuICBvdmVyZmxvdzogaGlkZGVuO1xyXG4gIG1heC13aWR0aDogMTAwJTtcclxuICAtLW9uZS1zZXZlbnRoLXdpZHRoOiAwcHg7XHJcbn1cclxuXHJcbi50YWJsZSB7XHJcbiAgd2lkdGg6IDEwMCU7XHJcbiAgYm9yZGVyLWNvbGxhcHNlOiBjb2xsYXBzZTtcclxuICB0ZXh0LWFsaWduOiBsZWZ0O1xyXG59XHJcblxyXG4udGFibGUtaGVhZGVyIHtcclxuICBiYWNrZ3JvdW5kLWNvbG9yOiB2YXIoLS1iYWNrZ3JvdW5kLXNlY29uZGFyeSk7XHJcbn1cclxuXHJcbi50YWJsZSB0aCB7XHJcbiAgcGFkZGluZzogMTJweCAxNnB4O1xyXG4gIGZvbnQtc2l6ZTogc21hbGw7XHJcbiAgZm9udC13ZWlnaHQ6IDUwMDtcclxufSBcclxuXHJcbi50b3AtbGVmdCB7XHJcbiAgd2lkdGg6IDEyOHB4O1xyXG59XHJcblxyXG4udGFibGUgdGQge1xyXG4gIHBhZGRpbmc6IDEycHggMTZweDtcclxuICBmb250LXNpemU6IHNtYWxsO1xyXG4gIGhlaWdodDogNjhweDtcclxuICBib3JkZXItdG9wOiB2YXIoLS1ib3JkZXItd2lkdGgpIHNvbGlkIHZhcigtLWJhY2tncm91bmQtbW9kaWZpZXItYm9yZGVyKTtcclxufVxyXG5cclxuLnRhc2tjZWxsLWVuY2xvc3VyZSB7XHJcbiAgYm9yZGVyLWxlZnQ6IHZhcigtLWJvcmRlci13aWR0aCkgc29saWQgdmFyKC0tYmFja2dyb3VuZC1tb2RpZmllci1ib3JkZXIpO1xyXG59XHJcblxyXG4ucHJvamVjdC1pbnB1dCB7XHJcbiAgYm9yZGVyOiBub25lICFpbXBvcnRhbnQ7XHJcbiAgd2lkdGg6IDEwMCU7XHJcbn1cclxuXHJcbi5kYXRlIHtcclxuICBib3JkZXItbGVmdDogdmFyKC0tYm9yZGVyLXdpZHRoKSBzb2xpZCB2YXIoLS1iYWNrZ3JvdW5kLW1vZGlmaWVyLWJvcmRlcik7IFxyXG4gIHdpZHRoOiB2YXIoLS10YXNrY2VsbC1lbmNsb3N1cmUtd2lkdGgpO1xyXG59Il0sCiAgIm1hcHBpbmdzIjogIjtBQUNBO0FBQ0U7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUFBO0FBR0Y7QUFDRTtBQUNBO0FBQ0E7QUFBQTtBQUdGO0FBQ0U7QUFBQTtBQUdGO0FBQ0U7QUFDQTtBQUNBO0FBQUE7QUFHRjtBQUNFO0FBQUE7QUFHRjtBQUNFO0FBQ0E7QUFDQTtBQUNBO0FBQUE7QUFHRjtBQUNFO0FBQUE7QUFHRjtBQUNFO0FBQ0E7QUFBQTtBQUdGO0FBQ0U7QUFDQTtBQUFBOyIsCiAgIm5hbWVzIjogW10KfQo= */ +/*# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3R5bGVzLmNzcyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiLyogRGVmaW5lIGEgY3VzdG9tIGJvcmRlciBjb2xvciAqL1xyXG4udGFibGUtY29udGFpbmVyIHtcclxuICBib3JkZXI6IHZhcigtLWJvcmRlci13aWR0aCkgc29saWQgdmFyKC0tYmFja2dyb3VuZC1tb2RpZmllci1ib3JkZXIpOyAvKiBDdXN0b20gYm9yZGVyIGNvbG9yICovXHJcbiAgYm9yZGVyLXJhZGl1czogOHB4OyAvKiBSb3VuZGVkIGNvcm5lcnMgKi9cclxuICBvdmVyZmxvdzogaGlkZGVuO1xyXG4gIG1heC13aWR0aDogMTAwJTtcclxuICAtLW9uZS1zZXZlbnRoLXdpZHRoOiAwcHg7XHJcbn1cclxuXHJcbi50YWJsZSB7XHJcbiAgd2lkdGg6IDEwMCU7XHJcbiAgYm9yZGVyLWNvbGxhcHNlOiBjb2xsYXBzZTtcclxuICB0ZXh0LWFsaWduOiBsZWZ0O1xyXG59XHJcblxyXG4udGFibGUtaGVhZGVyIHtcclxuICBiYWNrZ3JvdW5kLWNvbG9yOiB2YXIoLS1iYWNrZ3JvdW5kLXNlY29uZGFyeSk7XHJcbn1cclxuXHJcbi50YWJsZSB0aCB7XHJcbiAgcGFkZGluZzogMTJweCAxNnB4O1xyXG4gIGZvbnQtc2l6ZTogc21hbGw7XHJcbiAgZm9udC13ZWlnaHQ6IDUwMDtcclxufSBcclxuXHJcbi50b3AtbGVmdCB7XHJcbiAgd2lkdGg6IDEyOHB4O1xyXG59XHJcblxyXG4udGFibGUgdGQge1xyXG4gIHBhZGRpbmc6IDEycHggMTZweDtcclxuICBmb250LXNpemU6IHNtYWxsO1xyXG4gIGhlaWdodDogNjhweDtcclxuICBib3JkZXItdG9wOiB2YXIoLS1ib3JkZXItd2lkdGgpIHNvbGlkIHZhcigtLWJhY2tncm91bmQtbW9kaWZpZXItYm9yZGVyKTtcclxufVxyXG5cclxuLnRhc2tjZWxsLWVuY2xvc3VyZSB7XHJcbiAgYm9yZGVyLWxlZnQ6IHZhcigtLWJvcmRlci13aWR0aCkgc29saWQgdmFyKC0tYmFja2dyb3VuZC1tb2RpZmllci1ib3JkZXIpO1xyXG59XHJcblxyXG4ucHJvamVjdC1pbnB1dCB7XHJcbiAgYm9yZGVyOiBub25lICFpbXBvcnRhbnQ7XHJcbiAgd2lkdGg6IDEwMCU7XHJcbn1cclxuXHJcbi5kYXRlIHtcclxuICBib3JkZXItbGVmdDogdmFyKC0tYm9yZGVyLXdpZHRoKSBzb2xpZCB2YXIoLS1iYWNrZ3JvdW5kLW1vZGlmaWVyLWJvcmRlcik75mdzIjogIjtBQUNBO0FBQ0U7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUFBO0FBR0Y7QUFDRTtBQUNBO0FBQ0E7QUFBQTtBQUdGO0FBQ0U7QUFBQTtBQUdGO0FBQ0U7QUFDQTtBQUNBO0FBQUE7QUFHRjtBQUNFO0FBQUE7QUFHRjtBQUNFO0FBQ0E7QUFDQTtBQUNBO0FBQUE7QUFHRjtBQUNFO0FBQUE7QUFHRjtBQUNFO0FBQ0E7QUFBQTtBQUdGO0FBQ0U7QUFDQTtBQUFBOyIsCiAgIm5hbWVzIjogW10KfQo= */