Compare commits

...

3 Commits

23 changed files with 200 additions and 122 deletions

BIN
bun.lockb

Binary file not shown.

28
eslint.config.js Normal file
View File

@ -0,0 +1,28 @@
import globals from "globals"
import pluginJs from "@eslint/js"
import tseslint from "typescript-eslint"
import pluginReact from "eslint-plugin-react"
export default [
{ files: ["./src/**/*.{js,mjs,cjs,ts,jsx,tsx}"] },
{ plugins: { react: pluginReact } },
{
languageOptions: {
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
globals: globals.browser,
},
},
pluginJs.configs.recommended,
...tseslint.configs.recommended,
pluginReact.configs.flat.recommended,
{
rules: {
"react/react-in-jsx-scope": "off",
"react/no-unknown-property": "off",
},
},
]

View File

@ -18,14 +18,21 @@
"tiny-markdown-editor": "^0.1.26" "tiny-markdown-editor": "^0.1.26"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.13.0",
"@tauri-apps/cli": "^1", "@tauri-apps/cli": "^1",
"autoprefixer": "^10.4.18", "autoprefixer": "^10.4.18",
"commitizen": "^4.3.1", "commitizen": "^4.3.1",
"cz-conventional-changelog": "^3.3.0", "cz-conventional-changelog": "^3.3.0",
"eslint": "^9.13.0",
"eslint-plugin-react": "^7.37.2",
"globals": "^15.11.0",
"postcss": "^8.4.35", "postcss": "^8.4.35",
"prettier": "^3.3.3",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"typescript": "^5.0.2", "typescript": "^5.0.2",
"typescript-eslint": "^8.11.0",
"vite": "^5.0.0", "vite": "^5.0.0",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-kaioken": "^0.13.1" "vite-plugin-kaioken": "^0.13.1"
}, },
"config": { "config": {

View File

@ -1,14 +1,12 @@
import { useClickOutside, useKeyStroke, useMouse } from "@kaioken-core/hooks"; import { useClickOutside, useMouse } from "@kaioken-core/hooks";
import { Portal, signal, useEffect, useRef } from "kaioken"; import { Portal, signal, useEffect, useRef } from "kaioken";
namespace ContextMenuPortal { interface ContextMenuPortalProps {
export interface Props { children: JSX.Children
children: JSX.Children open: boolean
open: boolean closeAction: (() => void) | null | undefined
closeAction: (() => void) | null | undefined
}
} }
export function ContextMenuPortal({ children, open, closeAction }: ContextMenuPortal.Props) { export function ContextMenuPortal({ children, open, closeAction }: ContextMenuPortalProps) {
const { mouse } = useMouse() const { mouse } = useMouse()
const pos = signal({ x: 0, y: 0 }) const pos = signal({ x: 0, y: 0 })
const ref = useRef<HTMLDivElement>(null) const ref = useRef<HTMLDivElement>(null)

View File

@ -6,15 +6,14 @@ import images, { ImageCardType } from "../signals/images"
import { updateLocalStorage } from "../utils/localStorage" import { updateLocalStorage } from "../utils/localStorage"
import { useThemeDetector } from "../utils/useThemeDetector" import { useThemeDetector } from "../utils/useThemeDetector"
import { ContextMenuPortal } from "./ContextMenuPortal" import { ContextMenuPortal } from "./ContextMenuPortal"
import { CardTypes } from "../types"
namespace ImageCard { interface ImageCardProps {
export interface ImageCardProps { key: ImageCardType['id']
key: ImageCardType['id'] data: ImageCardType
data: ImageCardType
}
} }
export function ImageCard({ key: itemKey, data: item }: ImageCard.ImageCardProps) { export function ImageCard({ key: itemKey, data: item }: ImageCardProps) {
const { debounce } = useDebounce() const { debounce } = useDebounce()
const pressed = signal(false) const pressed = signal(false)
const newX = useRef(0) const newX = useRef(0)
@ -27,7 +26,7 @@ export function ImageCard({ key: itemKey, data: item }: ImageCard.ImageCardProps
function debounceLSUpdate(time?: number) { function debounceLSUpdate(time?: number) {
debounce(() => { debounce(() => {
updateLocalStorage("images", images.images.value) updateLocalStorage(CardTypes.IMAGES, images.images).notify()
}, time) }, time)
} }
@ -90,7 +89,7 @@ export function ImageCard({ key: itemKey, data: item }: ImageCard.ImageCardProps
window.removeEventListener('mouseup', _handleResizeMouseUp) window.removeEventListener('mouseup', _handleResizeMouseUp)
} }
function _handleClose(_e: Event) { function _handleClose() {
ImagesSignal.default.removeImage(item.id) ImagesSignal.default.removeImage(item.id)
ImagesSignal.default.images.notify() ImagesSignal.default.images.notify()
debounceLSUpdate() debounceLSUpdate()
@ -161,7 +160,7 @@ export function ImageCard({ key: itemKey, data: item }: ImageCard.ImageCardProps
function ExpandIcon({ cb }: { function ExpandIcon({ cb }: {
cb: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null | undefined cb: ((this: GlobalEventHandlers, ev: MouseEvent) => void) | null | undefined
}) { }) {
const isDarkTheme = useThemeDetector() const isDarkTheme = useThemeDetector()
return ( return (

View File

@ -110,7 +110,7 @@ function getInitialPosition(canvasDimensions: typeof canvasDimentsion): ScrollTo
try { try {
initPosition = JSON.parse(localStorage.getItem("pos") ?? "") initPosition = JSON.parse(localStorage.getItem("pos") ?? "")
} catch (e) { } catch (e) {
console.error("no local storage for pos") console.error("no local storage for pos " + e)
} }
if (!initPosition) return defaultScroll if (!initPosition) return defaultScroll

View File

@ -2,14 +2,12 @@ import { Editor, Listener } from 'tiny-markdown-editor'
import './md.css' import './md.css'
import { useEffect, useRef } from "kaioken" import { useEffect, useRef } from "kaioken"
namespace MarkDownEditor { interface MarkDownEditorProps {
export interface Props { initial: string
initial: string onChange: Listener<'change'>
onChange: Listener<'change'>
}
} }
export function MarkDownEditor({ initial, onChange }: MarkDownEditor.Props) { export function MarkDownEditor({ initial, onChange }: MarkDownEditorProps) {
const elRef = useRef<HTMLDivElement>(null) const elRef = useRef<HTMLDivElement>(null)
useEffect(() => { useEffect(() => {

View File

@ -24,7 +24,7 @@ export function MiniMap() {
useEffect(() => { useEffect(() => {
function _handleScroll(_e: Event) { function _handleScroll() {
scrollX.value = window.scrollX scrollX.value = window.scrollX
scrollY.value = window.scrollY scrollY.value = window.scrollY
} }
@ -47,7 +47,7 @@ export function MiniMap() {
const image = images.images.value[imageKey] const image = images.images.value[imageKey]
const el = useRef(null) const el = useRef(null)
function _handleItemClick(_e: MouseEvent) { function _handleItemClick() {
const newLeft = image.position.x - ((viewportWidth / 2) - (image.dimensions.w / 2)) const newLeft = image.position.x - ((viewportWidth / 2) - (image.dimensions.w / 2))
const newTop = image.position.y - ((viewportHeight / 2) - (image.dimensions.h / 2)) const newTop = image.position.y - ((viewportHeight / 2) - (image.dimensions.h / 2))
@ -65,7 +65,7 @@ export function MiniMap() {
const newZIndex = LayerEnum.MINIMAP + 1 const newZIndex = LayerEnum.MINIMAP + 1
return ( return (
<div ref={el} className={"absolute dark:bg-green-500 bg-green-300 dark:hover:bg-blue-500 hover:bg-blue-300 cursor-pointer border dark:border-[#222] border-green-500 rounded"} <div key={imageKey} ref={el} className={"absolute dark:bg-green-500 bg-green-300 dark:hover:bg-blue-500 hover:bg-blue-300 cursor-pointer border dark:border-[#222] border-green-500 rounded"}
onclick={_handleItemClick} onclick={_handleItemClick}
style={{ style={{
width: `${newWidth}px`, width: `${newWidth}px`,
@ -82,7 +82,7 @@ export function MiniMap() {
{Object.keys(notes.notes.value).map((noteKey: NoteCardType['id']) => { {Object.keys(notes.notes.value).map((noteKey: NoteCardType['id']) => {
const note = notes.notes.value[noteKey] const note = notes.notes.value[noteKey]
function _handleItemClick(_e: MouseEvent) { function _handleItemClick() {
const newLeft = note.position.x - ((viewportWidth / 2) - (note.dimensions.w / 2)) const newLeft = note.position.x - ((viewportWidth / 2) - (note.dimensions.w / 2))
const newTop = note.position.y - ((viewportHeight / 2) - (note.dimensions.h / 2)) const newTop = note.position.y - ((viewportHeight / 2) - (note.dimensions.h / 2))
window.scrollTo({ window.scrollTo({
@ -99,7 +99,7 @@ export function MiniMap() {
const newZIndex = LayerEnum.MINIMAP + 1 const newZIndex = LayerEnum.MINIMAP + 1
return ( return (
<div className={"absolute dark:bg-gray-500 bg-gray-300 hover:bg-blue-500 cursor-pointer border dark:border-[#222] border-gray-400 rounded"} <div key={noteKey} className={"absolute dark:bg-gray-500 bg-gray-300 hover:bg-blue-500 cursor-pointer border dark:border-[#222] border-gray-400 rounded"}
onclick={_handleItemClick} onclick={_handleItemClick}
style={{ style={{
width: `${newWidth}px`, width: `${newWidth}px`,
@ -116,7 +116,7 @@ export function MiniMap() {
const text = texts.texts.value[textKey] const text = texts.texts.value[textKey]
const el = useRef(null) const el = useRef(null)
function _handleItemClick(_e: MouseEvent) { function _handleItemClick() {
window.scrollTo({ window.scrollTo({
left: text.position.x - ((viewportWidth / 2) - (text.dimensions.w / 2)), left: text.position.x - ((viewportWidth / 2) - (text.dimensions.w / 2)),
top: text.position.y - ((viewportHeight / 2) - (text.dimensions.h / 2)), top: text.position.y - ((viewportHeight / 2) - (text.dimensions.h / 2)),
@ -125,7 +125,7 @@ export function MiniMap() {
} }
return ( return (
<div ref={el} className={"bg-indigo-500 hover:bg-blue-500 cursor-pointer rounded"} <div key={textKey} ref={el} className={"bg-indigo-500 hover:bg-blue-500 cursor-pointer rounded"}
onclick={_handleItemClick} onclick={_handleItemClick}
style={{ style={{
position: 'absolute', position: 'absolute',

View File

@ -12,14 +12,12 @@ import { createFileAndExport } from "../utils/createFileAndExport"
import { ContextMenuPortal } from "./ContextMenuPortal" import { ContextMenuPortal } from "./ContextMenuPortal"
import { HelpIcon } from "./icons/HelpIcon" import { HelpIcon } from "./icons/HelpIcon"
namespace NoteCard { interface NoteCardProps {
export interface NoteCardProps { key: NoteCardType['id']
key: NoteCardType['id'] data: NoteCardType
data: NoteCardType
}
} }
export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) { export function NoteCard({ key: itemKey, data: item }: NoteCardProps) {
const saved = signal(true) const saved = signal(true)
const pressed = signal(false) const pressed = signal(false)
const newX = useRef(0) const newX = useRef(0)
@ -103,7 +101,7 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
saved.value = false saved.value = false
} }
function _handleClose(_e: Event) { function _handleClose() {
NotesSigal.default.removeNote(item.id) NotesSigal.default.removeNote(item.id)
NotesSigal.default.notes.notify() NotesSigal.default.notes.notify()
updateLocalStorage() updateLocalStorage()
@ -117,7 +115,7 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
createFileAndExport("Note", item.contents, "text/markdown") createFileAndExport("Note", item.contents, "text/markdown")
} }
function _handleExportClick(_e: MouseEvent) { function _handleExportClick() {
_exportFile() _exportFile()
} }
@ -149,11 +147,11 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
switch (e.key) { switch (e.key) {
case 'Delete': case 'Delete':
e.preventDefault() e.preventDefault()
_handleClose(e) _handleClose()
break break
case 'Backspace': case 'Backspace':
e.preventDefault() e.preventDefault()
_handleClose(e) _handleClose()
break break
case 'e': case 'e':
e.preventDefault() e.preventDefault()
@ -273,7 +271,7 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
} }
function ExpandIcon({ cb }: { function ExpandIcon({ cb }: {
cb: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null | undefined cb: ((this: GlobalEventHandlers, ev: MouseEvent) => void) | null | undefined
}) { }) {
const isDarkTheme = useThemeDetector() const isDarkTheme = useThemeDetector()

View File

@ -3,17 +3,15 @@ import { TextSignal, focusedItem } from "../signals"
import { useDebounce } from "../utils/useDebounce" import { useDebounce } from "../utils/useDebounce"
import texts, { TextCardType } from "../signals/texts" import texts, { TextCardType } from "../signals/texts"
import { LayerEnum } from "../utils/enums" import { LayerEnum } from "../utils/enums"
import { Card } from "../types" import { Card, CardTypes } from "../types"
import { useThemeDetector } from "../utils/useThemeDetector" import { useThemeDetector } from "../utils/useThemeDetector"
namespace TextItem { interface TextCardProps {
export interface TextCardProps { key: TextCardType['id']
key: TextCardType['id'] data: TextCardType
data: TextCardType
}
} }
export function TextItem({ key: itemKey, data: item }: TextItem.TextCardProps) { export function TextItem({ key: itemKey, data: item }: TextCardProps) {
const { debounce } = useDebounce() const { debounce } = useDebounce()
const pressed = signal(false) const pressed = signal(false)
const newX = useRef(0) const newX = useRef(0)
@ -28,7 +26,7 @@ export function TextItem({ key: itemKey, data: item }: TextItem.TextCardProps) {
const elDems = elRef.current?.getBoundingClientRect() const elDems = elRef.current?.getBoundingClientRect()
const elW = elDems?.width ?? 100 const elW = elDems?.width ?? 100
const elH = elDems?.height ?? 100 const elH = elDems?.height ?? 100
const newDems: Card<'texts'>['dimensions'] = { w: elW, h: elH } const newDems: Card<CardTypes.TEXTS>['dimensions'] = { w: elW, h: elH }
TextSignal.default.updateTextProperty(itemKey, 'dimensions', newDems) TextSignal.default.updateTextProperty(itemKey, 'dimensions', newDems)
TextSignal.default.texts.notify() TextSignal.default.texts.notify()
}, [elRef.current, item.fontSize]) }, [elRef.current, item.fontSize])
@ -37,7 +35,7 @@ export function TextItem({ key: itemKey, data: item }: TextItem.TextCardProps) {
function updateLocalStorage(time?: number) { function updateLocalStorage(time?: number) {
debounce(() => { debounce(() => {
localStorage.setItem("texts", JSON.stringify(texts.texts.value)) localStorage.setItem(CardTypes.TEXTS, JSON.stringify(texts.texts.value))
}, time) }, time)
} }
@ -87,7 +85,7 @@ export function TextItem({ key: itemKey, data: item }: TextItem.TextCardProps) {
window.addEventListener('mouseup', _handleResizeMouseUp) window.addEventListener('mouseup', _handleResizeMouseUp)
} }
function _handleResizeMouseUp(_e: MouseEvent) { function _handleResizeMouseUp() {
pressed.value = false pressed.value = false
window.removeEventListener('mousemove', _handleResizeMove) window.removeEventListener('mousemove', _handleResizeMove)
window.removeEventListener('mouseup', _handleResizeMouseUp) window.removeEventListener('mouseup', _handleResizeMouseUp)
@ -157,7 +155,7 @@ export function TextItem({ key: itemKey, data: item }: TextItem.TextCardProps) {
> >
<p <p
ref={pRef} ref={pRef}
//@ts-expect-error //@ts-expect-error oninput doesnt exist on the props type
oninput={_handleContentInput} oninput={_handleContentInput}
contentEditable contentEditable
className={'text-p inline-block px-2 w-full select-none drop-shadow relative'}> className={'text-p inline-block px-2 w-full select-none drop-shadow relative'}>
@ -171,13 +169,12 @@ export function TextItem({ key: itemKey, data: item }: TextItem.TextCardProps) {
) )
} }
namespace CloseIcon { interface CloseIconProps {
export interface Props { cb: ((this: GlobalEventHandlers, ev: MouseEvent) => void) | null | undefined,
cb: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null | undefined, item: TextSignal.TextCardType
item: TextSignal.TextCardType
}
} }
function CloseIcon({ item, cb }: CloseIcon.Props) {
function CloseIcon({ item, cb }: CloseIconProps) {
const isDark = useThemeDetector() const isDark = useThemeDetector()
return ( return (
<svg <svg
@ -204,13 +201,12 @@ function CloseIcon({ item, cb }: CloseIcon.Props) {
) )
} }
namespace ExpandIcon { interface ExpandIconProps {
export interface Props { cb: ((this: GlobalEventHandlers, ev: MouseEvent) => void) | null | undefined,
cb: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null | undefined, item: TextSignal.TextCardType
item: TextSignal.TextCardType
}
} }
function ExpandIcon({ cb, item }: ExpandIcon.Props) {
function ExpandIcon({ cb, item }: ExpandIconProps) {
const isDark = useThemeDetector() const isDark = useThemeDetector()
return ( return (
<svg <svg

View File

@ -8,6 +8,7 @@ import {
useEffect, useEffect,
useState, useState,
} from "kaioken" } from "kaioken"
import { noop } from "kaioken/utils"
type Toast = { type Toast = {
ts: number ts: number
@ -21,11 +22,15 @@ const defaultDuration = 3000
const ToastContext = createContext<{ const ToastContext = createContext<{
showToast: (type: Toast["type"], message: string) => void showToast: (type: Toast["type"], message: string) => void
}>(null as any) }>({ showToast: noop })
export const useToast = () => useContext(ToastContext) export const useToast = () => useContext(ToastContext)
export const ToastContextProvider: Kaioken.FC = ({ children }) => { interface ToastProviderProps {
children: JSX.Children
}
export function ToastContextProvider({ children }: ToastProviderProps) {
const [toasts, setToasts] = useState<Toast[]>([]) const [toasts, setToasts] = useState<Toast[]>([])
useEffect(() => { useEffect(() => {

View File

@ -1,5 +1,6 @@
import { ImagesSignal } from "../../signals" import { ImagesSignal } from "../../signals"
import images from "../../signals/images" import images from "../../signals/images"
import { CardTypes } from "../../types"
import { updateLocalStorage } from "../../utils/localStorage" import { updateLocalStorage } from "../../utils/localStorage"
import { useToast } from "../Toast" import { useToast } from "../Toast"
import { Tooltip } from "./Tooltip" import { Tooltip } from "./Tooltip"
@ -13,12 +14,16 @@ export function ImageCardButton() {
input.accept = "image/*" input.accept = "image/*"
input.multiple = false input.multiple = false
input.onchange = (e: any) => { input.onchange = (e: Event) => {
const file = e.target.files[0] const el = e.target as HTMLInputElement
if (!el.files?.length) return
if (!el.files.length) return
const file = el.files[0]
const reader = new FileReader() const reader = new FileReader()
reader.readAsDataURL(file) reader.readAsDataURL(file)
reader.onload = function(readerEvent) { reader.onload = function(readerEvent) {
let image = document.createElement('img') const image = document.createElement('img')
image.onload = function() { image.onload = function() {
const { width, height } = image const { width, height } = image
@ -33,7 +38,7 @@ export function ImageCardButton() {
if (!img) return if (!img) return
const imgId = ImagesSignal.default.addImage({ const imgId = ImagesSignal.default.addImage({
type: "image", type: CardTypes.IMAGES,
title: "New Image", title: "New Image",
contents: content as string, contents: content as string,
position: { position: {
@ -47,7 +52,7 @@ export function ImageCardButton() {
}) })
try { try {
updateLocalStorage("images", images.images.value) updateLocalStorage(CardTypes.IMAGES, images.images).notify()
} catch (e: unknown) { } catch (e: unknown) {
if (e instanceof DOMException) { if (e instanceof DOMException) {
if (e.name !== 'QuotaExceededError') return if (e.name !== 'QuotaExceededError') return

View File

@ -1,6 +1,7 @@
import images from "../../signals/images" import images, { ImageCardType } from "../../signals/images"
import notes from "../../signals/notes" import notes, { NoteCardType } from "../../signals/notes"
import { Card } from "../../types" import texts, { TextCardType } from "../../signals/texts"
import { Card, CardTypes } from "../../types"
import { convertBase64ToJson } from "../../utils/convertBase64ToJson" import { convertBase64ToJson } from "../../utils/convertBase64ToJson"
import { updateLocalStorage } from "../../utils/localStorage" import { updateLocalStorage } from "../../utils/localStorage"
import { Tooltip } from "./Tooltip" import { Tooltip } from "./Tooltip"
@ -9,36 +10,55 @@ import { defaultClassName } from "./utils"
export function ImportButton() { export function ImportButton() {
function _handleImport() { function _handleImport() {
// guard clause to prevent overwriting existing cards
if (images.images.value || notes.notes.value) {
const isConfirmed = confirm("Are you sure you want to overwrite your existing cards?")
if (!isConfirmed) return
}
const input = document.createElement('input') const input = document.createElement('input')
input.type = 'file' input.type = 'file'
input.accept = ".json" input.accept = ".json"
input.multiple = false input.multiple = false
input.onchange = (e: any) => { input.onchange = (e: Event) => {
const file = e.target.files[0] if (e.target === null) return
const el = e.target as HTMLInputElement
if (!el.files?.length) return
const file = el.files[0]
const reader = new FileReader() const reader = new FileReader()
reader.readAsDataURL(file) reader.readAsDataURL(file)
reader.onload = function(readerEvent) { reader.onload = function(readerEvent) {
let content = readerEvent.target?.result; let content = readerEvent.target?.result;
// get only the base64 parts and not any identifiers // get only the base64 parts and not any identifiers
content = (content as string).split(',')[1] content = (content as string).split(',')[1]
const data: Record<string, Card<'notes'> | Card<'images'>> = convertBase64ToJson(content) const data: Record<string, Card<CardTypes>> = convertBase64ToJson(content)
for (let key in data) { console.log(data)
for (const key in data) {
const item = data[key] const item = data[key]
if (item.type == 'images') { const { id, ...rest } = item
const { id, ...rest } = item console.log(id, rest)
images.addImage(rest) switch (item.type) {
} case CardTypes.IMAGES:
console.log("adding image: ", rest)
if (item.type == 'notes') { images.addImage(rest as ImageCardType)
const { id, ...rest } = item break;
notes.addNote(rest) case CardTypes.NOTES:
notes.addNote(rest as NoteCardType)
break;
case CardTypes.TEXTS:
texts.addText(rest as TextCardType)
break;
default:
break;
} }
} }
updateLocalStorage('notes', notes.notes.value) console.log("images: ", images.images.value)
updateLocalStorage('images', images.images.value)
notes.notes.notify() updateLocalStorage(CardTypes.NOTES, notes.notes).notify()
images.images.notify() updateLocalStorage(CardTypes.IMAGES, images.images).notify()
updateLocalStorage(CardTypes.TEXTS, texts.texts).notify()
} }
} }
input.click() input.click()

View File

@ -21,7 +21,7 @@ export function TextButton() {
h: 100 h: 100
} }
}) })
updateLocalStorage("texts", texts.texts.value) updateLocalStorage("texts", texts.texts).notify()
} }
return ( return (

View File

@ -1,9 +1,8 @@
namespace ExportIcon { interface ExportIconProps {
export interface Props { className?: string
className?: string
}
} }
export function ExportIcon({ className }: ExportIcon.Props) {
export function ExportIcon({ className }: ExportIconProps) {
return ( return (
<svg <svg

View File

@ -1,10 +1,9 @@
namespace HelpIcon { interface HelpIconProps {
export interface Props { onMouseOver?: () => void
onMouseOver?: () => void onMouseOut?: () => void
onMouseOut?: () => void
}
} }
export function HelpIcon({ onMouseOver, onMouseOut }: HelpIcon.Props) {
export function HelpIcon({ onMouseOver, onMouseOut }: HelpIconProps) {
return ( return (
<svg <svg

View File

@ -1,8 +1,8 @@
import { signal } from "kaioken" import { signal } from "kaioken"
import { Card } from "../types" import { Card, CardTypes } from "../types"
import { focusedItem } from "." import { focusedItem } from "."
export type ImageCardType = Card<"images"> export type ImageCardType = Card<CardTypes.IMAGES>
const images = signal<Record<ImageCardType["id"], ImageCardType>>({}) const images = signal<Record<ImageCardType["id"], ImageCardType>>({})
@ -15,6 +15,7 @@ function addImage(data: Omit<ImageCardType, "id">) {
...data, ...data,
id: crypto.randomUUID(), id: crypto.randomUUID(),
} }
console.log("adding image: ", newCard)
images.value[newCard.id] = newCard images.value[newCard.id] = newCard
images.notify() images.notify()
focusedItem.value = newCard.id focusedItem.value = newCard.id

View File

@ -1,8 +1,8 @@
import { signal } from "kaioken" import { signal } from "kaioken"
import { Card } from "../types" import { Card, CardTypes } from "../types"
import { focusedItem } from "." import { focusedItem } from "."
export type NoteCardType = Card<"notes"> export type NoteCardType = Card<CardTypes.NOTES>
const notes = signal<Record<NoteCardType["id"], NoteCardType>>({}) const notes = signal<Record<NoteCardType["id"], NoteCardType>>({})

View File

@ -1,8 +1,8 @@
import { signal } from "kaioken" import { signal } from "kaioken"
import { Card } from "../types" import { Card, CardTypes } from "../types"
import { focusedItem } from "." import { focusedItem } from "."
export type TextCardType = Card<"texts"> & { export type TextCardType = Card<CardTypes.TEXTS> & {
fontSize: number fontSize: number
} }

View File

@ -1,14 +1,19 @@
export type CardTypes = "notes" | "images" | "texts"
export type positionCoords = { x: number; y: number } export type positionCoords = { x: number; y: number }
export type dimensionCoords = { w: number; h: number } export type dimensionCoords = { w: number; h: number }
export enum CardTypes {
NOTES = "notes",
IMAGES = "images",
TEXTS = "texts",
}
type Base64 = string type Base64 = string
export interface Card<Ttype extends CardTypes> { export interface Card<TCard extends CardTypes> {
id: string id: string
type: Ttype type: TCard
title: string title: string
contents: Ttype extends "image" ? Base64 : string contents: TCard extends "image" ? Base64 : string
position: positionCoords position: positionCoords
dimensions: dimensionCoords dimensions: dimensionCoords
} }

View File

@ -1,8 +1,17 @@
import { CardTypes } from "../types" import { Signal } from "kaioken"
import { Card, CardTypes } from "../types"
export function updateLocalStorage( export function updateLocalStorage(
location: CardTypes, location: CardTypes,
collection: unknown[] | Record<string, unknown> collection: Signal<Record<string, Card<CardTypes>>>
) { ) {
localStorage.setItem(location, JSON.stringify(collection)) try {
localStorage.setItem(location, JSON.stringify(collection.value))
} catch (e) {
throw new DOMException(
"Could not update local storage " + e,
"LocalStorageError"
)
}
return collection
} }

View File

@ -2,12 +2,12 @@ import { sideEffectsEnabled, useHook } from "kaioken"
import { noop } from "kaioken/utils" import { noop } from "kaioken/utils"
type UseDebounceState = { type UseDebounceState = {
timer: number timer: Timer | undefined
debounce: (this: any, func: Function, timeout?: number) => void debounce: (func: (...args: unknown[]) => void, timeout?: number) => void
} }
function createState(): UseDebounceState { function createState(): UseDebounceState {
return { timer: 0, debounce: noop } return { timer: undefined, debounce: noop }
} }
export function useDebounce() { export function useDebounce() {
@ -17,8 +17,7 @@ export function useDebounce() {
if (!isInit) return { timer: hook.timer, debounce: hook.debounce } if (!isInit) return { timer: hook.timer, debounce: hook.debounce }
hook.debounce = function debounce( hook.debounce = function debounce(
this: any, func: (...args: unknown[]) => void,
func: Function,
timeout = 300 timeout = 300
) { ) {
clearTimeout(hook.timer) clearTimeout(hook.timer)

View File

@ -1,9 +1,21 @@
import { defineConfig } from "vite" import { defineConfig } from "vite"
import kaioken from "vite-plugin-kaioken" import kaioken from "vite-plugin-kaioken"
import eslint from "vite-plugin-eslint"
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [kaioken()], plugins: [
kaioken(),
eslint({
cache: true,
exclude: ["src-tauri/**/*"],
emitError: true,
emitWarning: true,
failOnError: false,
failOnWarning: false,
lintOnStart: true,
}),
],
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
// //
// 1. prevent vite from obscuring rust errors // 1. prevent vite from obscuring rust errors