Skip to content
66 changes: 66 additions & 0 deletions packages/pointer-editor/libs/api/ocr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
export const getMathpixKeys = () => {
const g = typeof globalThis !== 'undefined' ? globalThis : window;
const viteEnv =
typeof import.meta !== 'undefined' && import.meta && import.meta.env
? import.meta.env
: undefined;

const appId =
(g && g.__MATHPIX_APP_ID) ||
(viteEnv && viteEnv.VITE_MATHPIX_APP_ID) ||
(typeof process !== 'undefined' && process.env && process.env.NEXT_PUBLIC_MATHPIX_APP_ID) ||
'';

const appKey =
(g && g.__MATHPIX_API_KEY) ||
(viteEnv && viteEnv.VITE_MATHPIX_API_KEY) ||
(typeof process !== 'undefined' && process.env && process.env.NEXT_PUBLIC_MATHPIX_API_KEY) ||
'';

if (!appId || !appKey) throw new Error('Mathpix API 환경값이 설정되지 않았습니다.');
return { appId, appKey };
};

export const recognizeImageWithMathpix = async (imageUrl) => {
const { appId, appKey } = getMathpixKeys();
const res = await fetch('https://api.mathpix.com/v3/text', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
app_id: appId,
app_key: appKey,
},
body: JSON.stringify({
src: imageUrl,
formats: ['text', 'latex_styled'],
metadata: { improve_mathpix: false },
}),
});
if (!res.ok) {
const errText = await res.text().catch(() => '');
throw new Error(`Mathpix 요청 실패: ${res.status} ${res.statusText} ${errText}`);
}
const json = await res.json();
return json;
};

export const convertMathpixToDollar = (text) => {
if (!text) return '';
let output = text;
output = output.replace(/\\\[([\s\S]*?)\\\]/g, (_m, p1) => `$${p1.replace(/\s+/g, ' ').trim()}$`);
output = output.replace(/\\\(([\s\S]*?)\\\)/g, (_m, p1) => `$${p1.replace(/\s+/g, ' ').trim()}$`);
return output;
};

export const recognizeAndConvertMathpixText = async (imageUrl) => {
const json = await recognizeImageWithMathpix(imageUrl);
const converted = convertMathpixToDollar(json.text || '');
return converted;
};

export default {
getMathpixKeys,
recognizeImageWithMathpix,
convertMathpixToDollar,
recognizeAndConvertMathpixText,
};
7 changes: 6 additions & 1 deletion packages/pointer-editor/libs/assets/CloudUploadIcon.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { memo } from 'react';

const ColorIcon = (props) => (
<svg width='40' height='40' viewBox='0 0 40 40' fill='none' xmlns='http://www.w3.org/2000/svg'>
<svg
width={props.width ?? '40'}
height={props.height ?? '40'}
viewBox='0 0 40 40'
fill='none'
xmlns='http://www.w3.org/2000/svg'>
<path
d='M0 8C0 3.58172 3.58172 0 8 0H32C36.4183 0 40 3.58172 40 8V32C40 36.4183 36.4183 40 32 40H8C3.58172 40 0 36.4183 0 32V8Z'
fill='#3E3F45'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,27 +224,27 @@ const categories = [
symbols: [
{
icon: <AlphaIcon />,
latex: 'α',
latex: '\\alpha',
},
{
icon: <BetaIcon />,
latex: 'β',
latex: '\\beta',
},
{
icon: <GammaIcon />,
latex: 'γ',
latex: '\\gamma',
},
{
icon: <ThetaIcon />,
latex: 'θ',
latex: '\\theta',
},
{
icon: <PiIcon />,
latex: 'π',
latex: '\\pi',
},
{
icon: <OmegaIcon />,
latex: 'ω',
latex: '\\omega',
},
],
},
Expand All @@ -253,67 +253,67 @@ const categories = [
symbols: [
{
icon: <SmallSigmaIcon />,
latex: '',
latex: '\\Sigma',
},
{
icon: <SmallProductIcon />,
latex: '',
latex: '\\Pi',
},
{
icon: <SmallInterIcon />,
latex: '',
latex: '\\cap',
},
{
icon: <UnionIcon />,
latex: '',
latex: '\\cup',
},
{
icon: <SubsetIcon />,
latex: '',
latex: '\\subset',
},
{
icon: <SupersetIcon />,
latex: '',
latex: '\\supset',
},
{
icon: <SubsetEqIcon />,
latex: '',
latex: '\\subseteq',
},
{
icon: <SupersetEqIcon />,
latex: '',
latex: '\\supseteq',
},
{
icon: <ElementOfIcon />,
latex: '',
latex: '\\in',
},
{
icon: <ContainsIcon />,
latex: '',
latex: '\\ni',
},
{
icon: <LessEqualIcon />,
latex: '',
latex: '\\leq',
},
{
icon: <GreaterEqualIcon />,
latex: '',
latex: '\\geq',
},
{
icon: <MuchLessIcon />,
latex: '',
latex: '\\ll',
},
{
icon: <MuchGreaterIcon />,
latex: '',
latex: '\\gg',
},
{
icon: <PrecedesIcon />,
latex: '<',
latex: '\\prec',
},
{
icon: <SucceedsIcon />,
latex: '>',
latex: '\\succ',
},
],
},
Expand All @@ -322,51 +322,51 @@ const categories = [
symbols: [
{
icon: <PlusMinusIcon />,
latex: '±',
latex: '\\pm',
},
{
icon: <MinusPlusIcon />,
latex: '',
latex: '\\mp',
},
{
icon: <TimesIcon />,
latex: '×',
latex: '\\times',
},
{
icon: <DivideIcon />,
latex: '÷',
latex: '\\div',
},
{
icon: <CircleOperatorIcon />,
latex: '',
latex: '\\circ',
},
{
icon: <DegreeIcon />,
latex: '°',
latex: '\\degree',
},
{
icon: <ThereforeIcon />,
latex: '',
latex: '\\therefore',
},
{
icon: <BecauseIcon />,
latex: '',
latex: '\\because',
},
{
icon: <NotEqualIcon />,
latex: '',
latex: '\\neq',
},
{
icon: <SimilarEqualIcon />,
latex: '',
latex: '\\sim',
},
{
icon: <CongruentIcon />,
latex: '',
latex: '\\cong',
},
{
icon: <InfinityIcon />,
latex: '',
latex: '\\infty',
},
],
},
Expand All @@ -375,11 +375,11 @@ const categories = [
symbols: [
{
icon: <TriangleIcon />,
latex: '',
latex: '\\triangle',
},
{
icon: <AngleIcon />,
latex: '',
latex: '\\angle',
},
],
},
Expand All @@ -392,19 +392,19 @@ const categories = [
symbols: [
{
icon: <LimIcon />,
latex: 'lim _{ } { }',
latex: '\\lim_{ } { }',
},
{
icon: <LimArrowIcon />,
latex: 'lim _{ -> } { }',
latex: '\\lim_{ \\to } { }',
},
{
icon: <LimToZeroIcon />,
latex: 'lim _{ ->0} { }',
latex: '\\lim_{ \\to 0} { }',
},
{
icon: <LimToInfinityIcon />,
latex: 'lim _{ ->inf} { }',
latex: '\\lim_{ \\to \\infty} { }',
},
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const FormulaModal = ({ isOpen, onClose, onSave, initialValue = '' }) => {
useEffect(() => {
if (formula) {
try {
const rendered = katex.renderToString(formula, { throwOnError: false });
const rendered = katex.renderToString(formula, { throwOnError: false, displayMode: true });
setPreview(rendered);
} catch {
setPreview('수식 오류');
Expand Down Expand Up @@ -102,6 +102,7 @@ const FormulaModal = ({ isOpen, onClose, onSave, initialValue = '' }) => {
border: '1px solid #ccc',
borderRadius: '4px',
fontSize: '14px',
fontFamily: 'monospace',
}}
autoFocus
/>
Expand Down
Loading
Loading