9 Commits

10 changed files with 1359 additions and 1261 deletions

View File

@ -13,14 +13,14 @@ jobs:
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v3
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v2
with: with:
version: 8 version: 8
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
@ -31,7 +31,7 @@ jobs:
run: pnpm build run: pnpm build
- name: Upload production-ready build files - name: Upload production-ready build files
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: production-files name: production-files
path: ./dist path: ./dist
@ -44,7 +44,7 @@ jobs:
steps: steps:
- name: Download artifact - name: Download artifact
uses: actions/download-artifact@v4 uses: actions/download-artifact@v3
with: with:
name: production-files name: production-files
path: ./dist path: ./dist

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="JavaScriptLibraryMappings"> <component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="Node.js Core" /> <file url="PROJECT" libraries="{latest}" />
</component> </component>
</project> </project>

1
.idea/martials.no.iml generated
View File

@ -8,5 +8,6 @@
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="latest" level="application" />
</component> </component>
</module> </module>

View File

@ -7,9 +7,14 @@
<title>Hjem | Martials.no</title> <title>Hjem | Martials.no</title>
<meta name="description" content="Hjemmeside for API og diverse" /> <meta name="description" content="Hjemmeside for API og diverse" />
<link rel="icon" type="image/x-icon" href="resources/code.svg" /> <link rel="icon" type="image/x-icon" href="resources/code.svg" />
<!-- 100% privacy-first analytics -->
<script data-collect-dnt="true" async defer src="https://scripts.simpleanalyticscdn.com/latest.js"></script>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>
You need to enable JavaScript to run this app.
<img src="https://queue.simpleanalyticscdn.com/noscript.gif?collect-dnt=true" alt="" referrerpolicy="no-referrer-when-downgrade" />
</noscript>
<div id="root"></div> <div id="root"></div>
<script src="/src/app.tsx" type="module"></script> <script src="/src/app.tsx" type="module"></script>

View File

@ -12,21 +12,21 @@
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"postcss": "^8.4.38", "postcss": "^8.4.39",
"prettier": "3.3.1", "prettier": "3.3.3",
"prettier-plugin-tailwindcss": "^0.6.2", "prettier-plugin-tailwindcss": "^0.6.5",
"tailwindcss": "^3.4.4", "tailwindcss": "^3.4.6",
"typescript": "^5.4.5", "typescript": "^5.5.3",
"vite": "^5.2.13", "vite": "^5.3.4",
"vite-plugin-solid": "^2.10.2" "vite-plugin-solid": "^2.10.2"
}, },
"dependencies": { "dependencies": {
"@solidjs/router": "^0.13.5", "@solidjs/router": "^0.14.1",
"@types/diff": "^5.2.1", "@types/diff": "^5.2.1",
"diff": "^5.2.0", "diff": "^5.2.0",
"solid-headless": "^0.13.1", "solid-headless": "^0.13.1",
"solid-heroicons": "^3.2.4", "solid-heroicons": "^3.2.4",
"solid-js": "^1.8.17", "solid-js": "^1.8.18",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz" "xlsx": "^0.18.5"
} }
} }

2446
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@ import { For } from "solid-js/web"
import { type Component } from "solid-js" import { type Component } from "solid-js"
interface TruthTableProps extends SimpleProps { interface TruthTableProps extends SimpleProps {
table?: Table table?: TruthMatrix
header?: string[] header?: string[]
} }
@ -20,8 +20,7 @@ const TruthTable: Component<TruthTableProps> = ({ table, header, className, styl
<th <th
scope={"col"} scope={"col"}
class={ class={
`sticky top-0 bg-default-bg text-center outline `sticky top-0 bg-default-bg text-center outline outline-2 outline-offset-[-1px] outline-gray-500 [position:-webkit-sticky;]` /*TODO sticky header at the top of the screen */
outline-2 outline-offset-[-1px] outline-gray-500 [position:-webkit-sticky;]` /*TODO sticky header at the top of the screen */
} }
> >
<p class={"w-max px-2"}>{exp}</p> <p class={"w-max px-2"}>{exp}</p>
@ -37,8 +36,7 @@ const TruthTable: Component<TruthTableProps> = ({ table, header, className, styl
<For each={row}> <For each={row}>
{(value) => ( {(value) => (
<td <td
class={`border border-gray-500 text-center last:underline class={`border border-gray-500 text-center last:underline ${value ? "bg-green-700" : "bg-red-700"}`}
${value ? "bg-green-700" : "bg-red-700"}`}
> >
<p>{value ? "T" : "F"}</p> <p>{value ? "T" : "F"}</p>
</td> </td>

View File

@ -25,18 +25,20 @@ type Option = {
} }
const fetchUrls = [ const fetchUrls = [
"http://localhost:8080/simplify/table/", "http://localhost:8000/simplify/table/",
"https://api.martials.no/simplify-truths/simplify/table/" "https://api.martials.no/simplify-truths/v2/simplify/table/"
] ]
// TODO move some code to new components // TODO move some code to new components
// TODO option to ignore case
// TODO more user friendly options
const TruthTablePage: Component = () => { const TruthTablePage: Component = () => {
const [searchParams, setSearchParams] = useSearchParams() const [searchParams, setSearchParams] = useSearchParams()
let inputElement: HTMLInputElement | undefined = undefined let inputElement!: HTMLInputElement
let simplifyDefault = searchParams.simplify === undefined || searchParams.simplify === "true", let simplifyDefault = searchParams.simplify === undefined || searchParams.simplify === "true",
inputContent = !!searchParams.exp, inputContent = !!searchParams.exp,
hideIntermediate = searchParams.hideIntermediate === "true" hideIntermediate = searchParams.hideIntermediateSteps === "true"
const [simplifyEnabled, setSimplifyEnabled] = createSignal(simplifyDefault) const [simplifyEnabled, setSimplifyEnabled] = createSignal(simplifyDefault)
const [fetchResult, setFetchResult] = createSignal<FetchResult | null>(null) const [fetchResult, setFetchResult] = createSignal<FetchResult | null>(null)
@ -75,14 +77,14 @@ const TruthTablePage: Component = () => {
simplify: simplifyEnabled(), simplify: simplifyEnabled(),
hide: hideValues().value, hide: hideValues().value,
sort: sortValues().value, sort: sortValues().value,
hideIntermediate: hideIntermediates() hideIntermediateSteps: hideIntermediates()
}) })
getFetchResult(exp) void getFetchResult(exp)
} }
} }
function getFetchResult(exp: string | null): void { async function getFetchResult(exp: string | null): Promise<void> {
setFetchResult(null) setFetchResult(null)
if (exp && exp !== "") { if (exp && exp !== "") {
@ -90,18 +92,30 @@ const TruthTablePage: Component = () => {
setError(null) setError(null)
setIsLoaded(false) setIsLoaded(false)
fetch(`${fetchUrls[useLocalhost() ? 0 : 1]}${encodeURIComponent(exp)}? try {
simplify=${simplifyEnabled()}&hide=${hideValues().value}&sort=${sortValues().value}&caseSensitive=false& const response =
hideIntermediate=${hideIntermediates()}`) await fetch(`${fetchUrls[useLocalhost() ? 0 : 1]}${encodeURIComponent(exp)}?
.then((res) => res.json()) simplify=${simplifyEnabled()}&hide=${hideValues().value}&sort=${sortValues().value}&ignoreCase=false&
.then((res) => { hideIntermediateSteps=${hideIntermediates()}`)
if (res.status !== "OK" && !res.ok) {
return setError({ title: "Input error", message: res.message }) const body = await response.json()
} if (!response.ok) {
return setFetchResult(res) setError({
title: "Input error",
message: body.message
}) })
.catch((err) => setError({ title: "Fetch error", message: err.toString() })) } else {
.finally(() => setIsLoaded(true)) const fetchResult: FetchResult = body
setFetchResult(fetchResult)
}
} catch (e: any) {
setError({
title: "Error",
message: e.message
})
} finally {
setIsLoaded(true)
}
} }
} }
@ -120,7 +134,7 @@ hideIntermediate=${hideIntermediates()}`)
setSortValues(sortOptions.find((o) => o.value === sort) ?? sortOptions[0]) setSortValues(sortOptions.find((o) => o.value === sort) ?? sortOptions[0])
} }
getFetchResult(exp) void getFetchResult(exp)
} }
// Focuses searchbar on load // Focuses searchbar on load
@ -286,12 +300,12 @@ hideIntermediate=${hideIntermediates()}`)
/> />
</Show> </Show>
<Show when={simplifyEnabled() && (fetchResult()?.orderOperations?.length ?? 0) > 0} keyed> <Show when={simplifyEnabled() && (fetchResult()?.operations?.length ?? 0) > 0} keyed>
<ShowMeHow fetchResult={fetchResult} /> <ShowMeHow fetchResult={fetchResult} />
</Show> </Show>
</div> </div>
<Show when={isLoaded() && error() === null} keyed> <Show when={isLoaded() && error() === null && fetchResult()?.truthTable} keyed>
<Show when={simplifyEnabled()} keyed> <Show when={simplifyEnabled()} keyed>
<InfoBox <InfoBox
className={"mx-auto w-fit pb-1 text-center text-lg"} className={"mx-auto w-fit pb-1 text-center text-lg"}
@ -305,8 +319,8 @@ hideIntermediate=${hideIntermediates()}`)
<div class={"m-2 flex justify-center"}> <div class={"m-2 flex justify-center"}>
<div id={"table"} class={"h-[45rem] overflow-auto"}> <div id={"table"} class={"h-[45rem] overflow-auto"}>
<TruthTable <TruthTable
header={fetchResult()?.header ?? undefined} header={fetchResult()!.truthTable!.header}
table={fetchResult()?.table?.truthMatrix} table={fetchResult()!.truthTable!.truthMatrix}
id={tableId} id={tableId}
/> />
</div> </div>
@ -354,7 +368,7 @@ const ShowMeHow: Component<ShowMeHowProps> = ({ fetchResult }) => (
<MyDisclosure title={"Show me how it's done"}> <MyDisclosure title={"Show me how it's done"}>
<table class={"table"}> <table class={"table"}>
<tbody> <tbody>
<For each={fetchResult()?.orderOperations}>{orderOperationRow()}</For> <For each={fetchResult()?.operations}>{operationRow()}</For>
</tbody> </tbody>
</table> </table>
</MyDisclosure> </MyDisclosure>
@ -372,7 +386,8 @@ const HowTo: Component = () => (
Parentheses is also allowed. Parentheses is also allowed.
</p> </p>
<p> <p>
API docs can be found <Link to={"https://api.martials.no/simplify-truths"}>here</Link>. API docs can be found{" "}
<Link to={"https://api.martials.no/simplify-truths/v2/openapi"}>here</Link>.
</p> </p>
</MyDisclosure> </MyDisclosure>
@ -380,7 +395,7 @@ const HowTo: Component = () => (
</MyDisclosureContainer> </MyDisclosureContainer>
) )
const orderOperationRow = () => (operation: OrderOfOperation, index: Accessor<number>) => ( const operationRow = () => (operation: Operation, index: Accessor<number>) => (
<tr class={"border-b border-dotted border-gray-500"}> <tr class={"border-b border-dotted border-gray-500"}>
<td>{index() + 1}:</td> <td>{index() + 1}:</td>
<td class={"px-2"}> <td class={"px-2"}>
@ -437,7 +452,7 @@ const KeywordsDisclosure: Component = () => (
</tr> </tr>
<tr> <tr>
<td class={"pr-2"}>Implication:</td> <td class={"pr-2"}>Implication:</td>
<td>{"->"}</td> <td>{"=>"}</td>
<td class={"px-2"}>IMPLICATION</td> <td class={"px-2"}>IMPLICATION</td>
<td>IMP</td> <td>IMP</td>
</tr> </tr>

53
src/types/types.d.ts vendored
View File

@ -37,34 +37,51 @@ interface CardProps extends LinkProps {
title?: string title?: string
} }
type Expression = { type AtomicExpression = {
leading: string atomic: string
left: Expression | null
operator: Operator | null
right: Expression | null
trailing: string
atomic: string | null
} }
type Operator = "AND" | "OR" | "NOT" | "IMPLICATION" type NotExpression = {
not: Expression
}
type Table = boolean[][] type BinaryExpression = {
left: Expression
operator: BinaryOperator
right: Expression
}
type OrderOfOperation = { type Expression = AtomicExpression | NotExpression | BinaryExpression
type BinaryOperator = "AND" | "OR" | "IMPLICATION"
type Law =
| "ELIMINATION_OF_IMPLICATION"
| "DE_MORGANS_LAWS"
| "ABSORPTION_LAW"
| "ASSOCIATIVE_LAW"
| "DISTRIBUTIVE_LAW"
| "DOUBLE_NEGATION_ELIMINATION"
| "COMMUTATIVE_LAW"
type TruthMatrix = boolean[][]
type Operation = {
before: string before: string
after: string after: string
law: string law: Law
}
type Table = {
header: string[]
truthMatrix: TruthMatrix
} }
type FetchResult = { type FetchResult = {
status: string version: string
version: string | null
before: string before: string
after: string after: string
orderOperations: OrderOfOperation[] | null operations: Operation[]
expression: Expression | null expression: Expression | null
header: string[] | null truthTable?: Table | null
table: {
truthMatrix: Table
} | null
} }

View File

@ -7,8 +7,8 @@ export function replaceOperators(expression: string): string {
return expression return expression
.replaceAll(/\//g, "|") .replaceAll(/\//g, "|")
.replaceAll(/¬/g, "!") .replaceAll(/¬/g, "!")
.replaceAll(/\sOR\s/gi, " | ") .replaceAll(/\s(OR|)\s/gi, " | ")
.replaceAll(/\sAND\s/gi, " & ") .replaceAll(/\s(AND|⋀)\s/gi, " & ")
.replaceAll(/\s(IMPLICATION|IMP)\s/gi, " -> ") .replaceAll(/\s(IMPLICATION|IMP|->)\s/gi, " => ")
.replaceAll(/\sNOT\s/gi, " !") .replaceAll(/\sNOT\s/gi, " !")
} }