Compare commits

...
6 Commits
Author SHA1 Message Date
pantonshire ba97699af6 docker build fixes 2024-10-22 18:07:20 +01:00
pantonshire 0ed1c5ff75 update utfdump version 2024-10-22 17:33:50 +01:00
pantonshire 64119fdd99 remove redundant entrypoint from dockerfile 2023-07-02 17:36:13 +01:00
pantonshire e53ef3d9fc containerisation 2023-06-15 19:47:13 +01:00
pantonshire a9d73ecd57 safe area for iphone notch, button styling 2023-06-15 17:16:48 +01:00
pantonshire 232097d999 option to transpose table 2023-06-15 17:04:58 +01:00
7 changed files with 228 additions and 58 deletions

No files matched your search

+40
View File
@@ -0,0 +1,40 @@
FROM docker.io/library/rust:1.82-alpine AS wasm_builder
WORKDIR /app/
COPY utfdump/Cargo.toml utfdump/Cargo.lock ./
COPY utfdump/lib/ ./lib/
COPY utfdump/wasm/ ./wasm/
COPY utfdump/bin/ ./bin/
RUN apk update && apk add --no-cache musl-dev wasm-pack pkgconf openssl-dev openssl binaryen
WORKDIR /app/wasm/
RUN wasm-pack build --release --target web
FROM docker.io/library/node:20-alpine AS node
FROM node AS node_deps
WORKDIR /app/
COPY package.json package-lock.json ./
RUN npm ci
FROM node AS next_builder
WORKDIR /app/
COPY --from=wasm_builder /app/wasm/pkg/ ./wasm/utfdump/
COPY --from=node_deps /app/node_modules/ ./node_modules/
COPY package.json package-lock.json docker/next.config.js next-env.d.ts tsconfig.json ./
COPY src/ ./src/
COPY public/ ./public/
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build
FROM node AS runner
WORKDIR /app
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup -g 998 -S next && adduser -G next -u 998 -S next
COPY --from=next_builder /app/public/ ./public/
COPY --from=next_builder --chown=next:next /app/.next/standalone/ ./
COPY --from=next_builder --chown=next:next /app/.next/static/ ./.next/static/
USER next
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
+6
View File
@@ -0,0 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
};
module.exports = nextConfig;
+6
View File
@@ -1,4 +1,5 @@
:root {
--base_max_width: 1000px;
--main_1: #735cd6;
--main_2: #db52c9;
--light_1: #a598e0;
@@ -16,6 +17,11 @@ html,
body {
max-width: 100vw;
overflow-x: hidden;
padding:
env(safe-area-inset-top)
env(safe-area-inset-right)
env(safe-area-inset-bottom)
env(safe-area-inset-left);
}
a {
+19 -6
View File
@@ -3,7 +3,7 @@
}
.title_section {
max-width: 1000px;
max-width: var(--base_max_width);
margin: 0 auto 2rem auto;
padding: 0 3rem;
}
@@ -20,7 +20,7 @@
}
.input_section {
max-width: 1000px;
max-width: var(--base_max_width);
margin: 0 auto 2rem auto;
}
@@ -55,15 +55,28 @@
#output_table {
margin: 0 auto;
font-size: 0.9rem;
white-space: nowrap;
}
#output_table tbody th:nth-child(1) {
.output_table_regular tbody th:nth-child(1) {
text-align: center;
}
#output_table td,
#output_table th {
white-space: nowrap;
.output_table_transposed th,
.output_table_transposed td {
vertical-align: top;
}
.button_container {
max-width: var(--base_max_width);
margin: 1rem auto 1rem auto;
}
.button {
padding: 0.25rem 2rem;
display: block;
color: var(--main_1);
}
@media screen and (min-width: 40rem) {
+22 -2
View File
@@ -1,17 +1,33 @@
'use client';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import styles from '@/app/page.module.css'
import { TextField } from './textfield';
import { UtfdumpContextProvider } from '@/context/utfdump';
import { OutputTable } from './output_table';
const lsTransposeKey = 'transpose';
const lsTrueStr = 't';
const lsFalseStr = 'f';
type InspectorProps = {
sourceUrl: string,
};
export function Inspector(props: InspectorProps) {
const [currentString, setCurrentString] = useState('');
const [transpose, setTranspose] = useState<boolean>(true);
useEffect(() => {
const lsTranspose = localStorage.getItem(lsTransposeKey) !== lsFalseStr;
setTranspose(lsTranspose);
}, []);
function toggleTranspose() {
const newTranspose = !transpose;
setTranspose(newTranspose);
localStorage.setItem(lsTransposeKey, newTranspose ? lsTrueStr : lsFalseStr);
}
return (
<UtfdumpContextProvider>
@@ -30,7 +46,11 @@ export function Inspector(props: InspectorProps) {
</div>
</section>
<OutputTable currentString={currentString} />
<OutputTable
currentString={currentString}
transpose={transpose}
toggleTranspose={toggleTranspose}
/>
</section>
</UtfdumpContextProvider>
);
+134 -49
View File
@@ -3,10 +3,16 @@ import styles from '@/app/page.module.css'
import { UtfdumpContext } from '@/context/utfdump';
import { EncodedCodepoint } from 'utfdump_wasm';
export function OutputTable(props: { currentString: string }) {
export function OutputTable(props: {
currentString: string,
transpose: boolean,
toggleTranspose: () => void
}) {
const ctx = useContext(UtfdumpContext);
let nonEmpty = false;
let rows = [];
let transposedData: React.JSX.Element[][] = [[], [], [], [], [], [], [], [], [], [], [], [], [], []];
if (ctx.utfdump) {
for (const codepointStr of props.currentString) {
@@ -38,24 +44,60 @@ export function OutputTable(props: { currentString: string }) {
charDisplayStr = '\u25cc' + charDisplayStr;
}
rows.push((
<tr>
<th>{charDisplayStr}</th>
<td>{charData.name()}</td>
<td>U+{codepoint.toString(16).padStart(4, '0')}</td>
<td>{encodedUtf8Str}</td>
<td>{encodedUtf16Str}</td>
<td>{charData.category_full()}</td>
<td>{combiningClassName || combiningClassNum}</td>
<td>{charData.bidi_full()}</td>
<td>{charData.mirrored() ? 'Yes' : 'No'}</td>
<td>{optCharCodes(charData.decomp_string())}</td>
<td>{optCharCodes(charData.uppercase_string())}</td>
<td>{optCharCodes(charData.lowercase_string())}</td>
<td>{optCharCodes(charData.titlecase_string())}</td>
<td>{charData.numeric_value()}</td>
</tr>
));
const elemChar = <th>{charDisplayStr}</th>;
const elemName = <td>{charData.name()}</td>;
const elemCode = <td>U+{codepoint.toString(16).padStart(4, '0')}</td>;
const elemUtf8 = <td>{encodedUtf8Str}</td>;
const elemUtf16 = <td>{encodedUtf16Str}</td>;
const elemCategory = <td>{charData.category_full()}</td>;
const elemCombining = <td>{combiningClassName || combiningClassNum}</td>;
const elemBidi = <td>{charData.bidi_full()}</td>;
const elemMirrored = <td>{charData.mirrored() ? 'Yes' : 'No'}</td>;
const elemDecomp = <td>{optCharCodes(charData.decomp_string())}</td>;
const elemUpper = <td>{optCharCodes(charData.uppercase_string())}</td>;
const elemLower = <td>{optCharCodes(charData.lowercase_string())}</td>;
const elemTitle = <td>{optCharCodes(charData.titlecase_string())}</td>;
const elemNumeric = <td>{charData.numeric_value()}</td>;
if (props.transpose) {
transposedData[0].push(elemChar);
transposedData[1].push(elemName);
transposedData[2].push(elemCode);
transposedData[3].push(elemUtf8);
transposedData[4].push(elemUtf16);
transposedData[5].push(elemCategory);
transposedData[6].push(elemCombining);
transposedData[7].push(elemBidi);
transposedData[8].push(elemMirrored);
transposedData[9].push(elemDecomp);
transposedData[10].push(elemUpper);
transposedData[11].push(elemLower);
transposedData[12].push(elemTitle);
transposedData[13].push(elemNumeric);
}
else {
rows.push((
<tr>
{elemChar}
{elemName}
{elemCode}
{elemUtf8}
{elemUtf16}
{elemCategory}
{elemCombining}
{elemBidi}
{elemMirrored}
{elemDecomp}
{elemUpper}
{elemLower}
{elemTitle}
{elemNumeric}
</tr>
));
}
nonEmpty = true;
charData.free();
}
@@ -69,40 +111,83 @@ export function OutputTable(props: { currentString: string }) {
let table;
if (rows.length > 0) {
table = (
<table id={styles.output_table}>
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Code</th>
<th>UTF-8</th>
<th>UTF-16LE</th>
<th>Category</th>
<th>Combining</th>
<th>Bidirectional</th>
<th>Mirrored</th>
<th>Decomp</th>
<th>Upper</th>
<th>Lower</th>
<th>Title</th>
<th>Numeric</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
);
if (nonEmpty) {
if (props.transpose) {
table = (
<table id={styles.output_table} className={styles.output_table_transposed}>
<thead>
<tr><th></th>{transposedData[0]}</tr>
</thead>
<tbody>
<tr><th>Name</th>{transposedData[1]}</tr>
<tr><th>Code</th>{transposedData[2]}</tr>
<tr><th>UTF-8</th>{transposedData[3]}</tr>
<tr><th>UTF-16LE</th>{transposedData[4]}</tr>
<tr><th>Category</th>{transposedData[5]}</tr>
<tr><th>Combining</th>{transposedData[6]}</tr>
<tr><th>Bidirectional</th>{transposedData[7]}</tr>
<tr><th>Mirrored</th>{transposedData[8]}</tr>
<tr><th>Decomp</th>{transposedData[9]}</tr>
<tr><th>Upper</th>{transposedData[10]}</tr>
<tr><th>Lower</th>{transposedData[11]}</tr>
<tr><th>Title</th>{transposedData[12]}</tr>
<tr><th>Numeric</th>{transposedData[13]}</tr>
</tbody>
</table>
);
}
else {
table = (
<table id={styles.output_table} className={styles.output_table_regular}>
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Code</th>
<th>UTF-8</th>
<th>UTF-16LE</th>
<th>Category</th>
<th>Combining</th>
<th>Bidirectional</th>
<th>Mirrored</th>
<th>Decomp</th>
<th>Upper</th>
<th>Lower</th>
<th>Title</th>
<th>Numeric</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
);
}
} else {
table = (<></>);
}
let transposeButton;
if (nonEmpty) {
transposeButton = (
<button className={styles.button} onClick={props.toggleTranspose}>
Flip table
</button>
);
} else {
transposeButton = (<></>);
}
return (
<section className={styles.table_container}>
<div className={styles.overflow_scroll}>
{table}
<section className={styles.output_section}>
<div className={styles.button_container}>
{transposeButton}
</div>
<div className={styles.table_container}>
<div className={styles.overflow_scroll}>
{table}
</div>
</div>
</section>
);
+1 -1
Submodule utfdump updated: e90b77256a...69660a7e10.