diff --git a/package.json b/package.json index 8eb9db4..3b35b73 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,14 @@ "@tailwindcss/typography": "^0.5.15", "astro": "5.0.0-beta.5", "astro-icon": "^1.1.1", + "diff": "^7.0.0", "sharp": "^0.33.5", "svelte": "^4.2.19", "tailwindcss": "^3.4.13", "typescript": "^5.6.3" }, "devDependencies": { + "@types/diff": "^5.2.3", "daisyui": "^4.12.13", "prettier": "^3.3.3", "prettier-plugin-astro": "^0.14.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5748894..332dcab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,9 @@ importers: astro-icon: specifier: ^1.1.1 version: 1.1.1 + diff: + specifier: ^7.0.0 + version: 7.0.0 sharp: specifier: ^0.33.5 version: 0.33.5 @@ -57,6 +60,9 @@ importers: specifier: ^5.6.3 version: 5.6.3 devDependencies: + '@types/diff': + specifier: ^5.2.3 + version: 5.2.3 daisyui: specifier: ^4.12.13 version: 4.12.13(postcss@8.4.47) @@ -965,6 +971,9 @@ packages: '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/diff@5.2.3': + resolution: {integrity: sha512-K0Oqlrq3kQMaO2RhfrNQX5trmt+XLyom88zS0u84nnIcLvFnRUMRRHmrGny5GSM+kNO9IZLARsdQHDzkhAgmrQ==} + '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -1428,6 +1437,10 @@ packages: resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} engines: {node: '>=0.3.1'} + diff@7.0.0: + resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==} + engines: {node: '>=0.3.1'} + dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} @@ -4057,6 +4070,8 @@ snapshots: dependencies: '@types/ms': 0.7.34 + '@types/diff@5.2.3': {} + '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.6 @@ -4584,6 +4599,8 @@ snapshots: diff@5.2.0: {} + diff@7.0.0: {} + dlv@1.1.3: {} dom-serializer@2.0.0: diff --git a/src/components/HardwarePage.svelte b/src/components/HardwarePage.svelte index c9b1cee..67f6382 100644 --- a/src/components/HardwarePage.svelte +++ b/src/components/HardwarePage.svelte @@ -2,8 +2,9 @@ import Select from "./Select.svelte" import * as m from "@/paraglide/messages" import CollapseList from "@/components/collapse/CollapseList.svelte" + import type { CollectionEntry } from "astro:content" - export let hardware: any[] = [] + export let hardware: CollectionEntry<"hardware">[] = [] const hardwareOptions = hardware.map((item) => ({ key: item.id, diff --git a/src/components/Input.astro b/src/components/Input.astro deleted file mode 100644 index f3f1aed..0000000 --- a/src/components/Input.astro +++ /dev/null @@ -1,28 +0,0 @@ ---- -interface Props { - label: string - type?: "text" | "email" | "password" | "number" - name: string - required?: boolean - placeholder?: string -} - -const { - label, - type = "text", - name, - required = false, - placeholder, -} = Astro.props ---- - - diff --git a/src/components/Input.svelte b/src/components/Input.svelte new file mode 100644 index 0000000..3f952d5 --- /dev/null +++ b/src/components/Input.svelte @@ -0,0 +1,19 @@ + + + diff --git a/src/components/Menu.svelte b/src/components/Menu.svelte new file mode 100644 index 0000000..f952ff2 --- /dev/null +++ b/src/components/Menu.svelte @@ -0,0 +1,7 @@ + + +
+ +
diff --git a/src/components/Row.svelte b/src/components/Row.svelte new file mode 100644 index 0000000..27fb5dc --- /dev/null +++ b/src/components/Row.svelte @@ -0,0 +1,7 @@ + + +
+ +
diff --git a/src/components/Switch.svelte b/src/components/Switch.svelte new file mode 100644 index 0000000..cd1f496 --- /dev/null +++ b/src/components/Switch.svelte @@ -0,0 +1,29 @@ + + + diff --git a/src/components/buttons/Button.svelte b/src/components/buttons/Button.svelte new file mode 100644 index 0000000..ce9ec8b --- /dev/null +++ b/src/components/buttons/Button.svelte @@ -0,0 +1,11 @@ + + + diff --git a/src/components/ContactMeForm.astro b/src/components/contactMe/ContactMeForm.astro similarity index 93% rename from src/components/ContactMeForm.astro rename to src/components/contactMe/ContactMeForm.astro index 84f0970..e75096e 100644 --- a/src/components/ContactMeForm.astro +++ b/src/components/contactMe/ContactMeForm.astro @@ -1,5 +1,5 @@ --- -import Input from "@/components/Input.astro" +import Input from "@/components/Input.svelte" import * as m from "@/paraglide/messages.js" // TODO self-host email server --- diff --git a/src/components/contactMe/ContactMePage.astro b/src/components/contactMe/ContactMePage.astro new file mode 100644 index 0000000..280cf21 --- /dev/null +++ b/src/components/contactMe/ContactMePage.astro @@ -0,0 +1,7 @@ +--- +import ContactMeForm from "./ContactMeForm.astro" +import * as m from "@/paraglide/messages" +--- + +

{m.contactMe()}

+ diff --git a/src/components/icons/PajamasIcon.astro b/src/components/icons/PajamasIcon.astro index 5158da8..12d021b 100644 --- a/src/components/icons/PajamasIcon.astro +++ b/src/components/icons/PajamasIcon.astro @@ -2,6 +2,7 @@ import type { PajamasIcon } from "@/types/icons" import type { ComponentProps } from "@/types/props" import { Icon } from "astro-icon/components" + interface Props extends ComponentProps { name: PajamasIcon "aria-label": string diff --git a/src/components/icons/PajamasIcon.svelte b/src/components/icons/PajamasIcon.svelte new file mode 100644 index 0000000..818b9e7 --- /dev/null +++ b/src/components/icons/PajamasIcon.svelte @@ -0,0 +1,10 @@ + + + diff --git a/src/components/simplifyTruths/HowTo.astro b/src/components/simplifyTruths/HowTo.astro new file mode 100644 index 0000000..987a76a --- /dev/null +++ b/src/components/simplifyTruths/HowTo.astro @@ -0,0 +1,26 @@ +--- +import KeywordsDisclosure from "./KeywordsDisclosure.astro" +import DisclosureContainer from "./output/DisclosureContainer.astro" +import Disclosure from "./output/Disclosure.astro" +import ExternalLink from "../links/ExternalLink.astro" +// TODO translate and move link +--- + + + +

+ Fill in a truth expression and it will be simplified for you as much as possible. It will + also genereate a truth table with all possible values. You can use a single letter, word or + multiple words without spacing for each atomic value. If you do not want to simplify the + expression, simply turn off the toggle. Keywords for operators are defined below. + Parentheses is also allowed. +

+

+ API docs can be found + here + . +

+
+ + +
diff --git a/src/components/simplifyTruths/KeywordsDisclosure.astro b/src/components/simplifyTruths/KeywordsDisclosure.astro new file mode 100644 index 0000000..05a361b --- /dev/null +++ b/src/components/simplifyTruths/KeywordsDisclosure.astro @@ -0,0 +1,40 @@ +--- +import Disclosure from "./output/Disclosure.astro" +// TODO Translate +--- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameAPIOther
Not:!NOT
And:&AND
Or:|/OR
Implication:{"->"}IMPLICATIONIMP
+
\ No newline at end of file diff --git a/src/components/simplifyTruths/Search.svelte b/src/components/simplifyTruths/Search.svelte new file mode 100644 index 0000000..7bedc08 --- /dev/null +++ b/src/components/simplifyTruths/Search.svelte @@ -0,0 +1,24 @@ + + + + } + trailing={ + + + + } +/> diff --git a/src/components/simplifyTruths/ShowMeHow.astro b/src/components/simplifyTruths/ShowMeHow.astro new file mode 100644 index 0000000..9a9f23c --- /dev/null +++ b/src/components/simplifyTruths/ShowMeHow.astro @@ -0,0 +1,37 @@ +--- +import type { FetchResult } from "@/types/types" +import Disclosure from "./output/Disclosure.astro" +import DisclosureContainer from "./output/DisclosureContainer.astro" +import { diffChars } from "diff" + +interface Props { + fetchResult: FetchResult | null +} +const { fetchResult } = Astro.props +--- + + + + + + { + fetchResult?.orderOperations?.map((operation, index) => ( + + + + + + )) + } + +
{index + 1}: + { + diffChars(operation.before, operation.after).map((part) => ( + + {part.value} + + )) + } + using: {operation.law}
+
+
diff --git a/src/components/simplifyTruths/SimplifyTruthsPage.astro b/src/components/simplifyTruths/SimplifyTruthsPage.astro new file mode 100644 index 0000000..b7a86b3 --- /dev/null +++ b/src/components/simplifyTruths/SimplifyTruthsPage.astro @@ -0,0 +1,10 @@ +--- +import SimplifyTruthsPageBody from "./SimplifyTruthsPageBody.svelte" +import HowTo from "./HowTo.astro" +--- + +
+ + + +
\ No newline at end of file diff --git a/src/components/simplifyTruths/SimplifyTruthsPage.tsx b/src/components/simplifyTruths/SimplifyTruthsPage.tsx new file mode 100644 index 0000000..bffca65 --- /dev/null +++ b/src/components/simplifyTruths/SimplifyTruthsPage.tsx @@ -0,0 +1,428 @@ +/* @refresh reload */ + +type Option = { + name: string + value: "NONE" | "TRUE" | "FALSE" | "DEFAULT" | "TRUE_FIRST" | "FALSE_FIRST" +} + +const fetchUrls = [ + "http://localhost:8080/simplify/table/", + "https://api.martials.no/simplify-truths/simplify/table/" +] + +// TODO move some code to new components +const TruthTablePage: Component = () => { + const [searchParams, setSearchParams] = useSearchParams() + let inputElement: HTMLInputElement | undefined = undefined + + let simplifyDefault = searchParams.simplify === undefined || searchParams.simplify === "true", + inputContent = !!searchParams.exp, + hideIntermediate = searchParams.hideIntermediate === "true" + + const [simplifyEnabled, setSimplifyEnabled] = createSignal(simplifyDefault) + const [fetchResult, setFetchResult] = createSignal(null) + + const hideOptions: Option[] = [ + { name: "Show all result", value: "NONE" }, + { name: "Hide true results", value: "TRUE" }, + { name: "Hide false results", value: "FALSE" } + ] + + const [hideValues, setHideValues] = createSignal(hideOptions[0]) + + const sortOptions: Option[] = [ + { name: "Sort by default", value: "DEFAULT" }, + { name: "Sort by true first", value: "TRUE_FIRST" }, + { name: "Sort by false first", value: "FALSE_FIRST" } + ] + + const [sortValues, setSortValues] = createSignal(sortOptions[0]) + const [hideIntermediates, setHideIntermediates] = createSignal(hideIntermediate) + const [isLoaded, setIsLoaded] = createSignal(null) + const [error, setError] = createSignal<{ title: string; message: string } | null>(null) + const [useLocalhost, setUseLocalhost] = createSignal(false) + + /** + * Updates the state of the current expression to the new search with all whitespace removed. + * If the element is not found, reset. + */ + function onClick(e: Event): void { + e.preventDefault() // Stops the page from reloading onClick + const exp = inputElement?.value + + if (exp) { + setSearchParams({ + exp, + simplify: simplifyEnabled(), + hide: hideValues().value, + sort: sortValues().value, + hideIntermediate: hideIntermediates() + }) + + getFetchResult(exp) + } + } + + function getFetchResult(exp: string | null): void { + setFetchResult(null) + + if (exp && exp !== "") { + exp = replaceOperators(exp) + setError(null) + setIsLoaded(false) + + fetch(`${fetchUrls[useLocalhost() ? 0 : 1]}${encodeURIComponent(exp)}? +simplify=${simplifyEnabled()}&hide=${hideValues().value}&sort=${sortValues().value}&caseSensitive=false& +hideIntermediate=${hideIntermediates()}`) + .then((res) => res.json()) + .then((res) => { + if (res.status !== "OK" && !res.ok) { + return setError({ title: "Input error", message: res.message }) + } + return setFetchResult(res) + }) + .catch((err) => setError({ title: "Fetch error", message: err.toString() })) + .finally(() => setIsLoaded(true)) + } + } + + onMount((): void => { + if (searchParams.exp) { + const exp = searchParams.exp + if (exp && inputElement) { + inputElement.value = exp + } + const hide = searchParams.hide + if (hide) { + setHideValues(hideOptions.find((o) => o.value === hide) ?? hideOptions[0]) + } + const sort = searchParams.sort + if (sort) { + setSortValues(sortOptions.find((o) => o.value === sort) ?? sortOptions[0]) + } + + getFetchResult(exp) + } + + // Focuses searchbar on load + if (!isTouch()) { + inputElement?.focus() + } + }) + + const tableId = "truth-table" + const filenameId = "excel-filename" + + function _exportToExcel(): void { + const value = getElementById(filenameId)?.value + exportToExcel({ + name: value !== "" ? value : undefined, + tableId + }) + } + + return ( + + + (DEV) Use localhost: + + + +
+
+ + +
+ + +
+ + + + +

{fetchResult()?.after}

+
+
+ +
+
+ +
+
+
+
+
+ ) +} + +export default TruthTablePage + +interface SingleMenuItem { + option: Option + currentValue?: Accessor