Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions GPTutor-Frontend/npm_install.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
npm error code ERESOLVE
npm error ERESOLVE could not resolve
npm error
npm error While resolving: @happysanta/router@0.3.1
npm error Found: react@18.2.0
npm error node_modules/react
npm error react@"18.2.0" from the root project
npm error peer react@">=16.8.0" from @floating-ui/react-dom@2.0.8
npm error node_modules/@floating-ui/react-dom
npm error @floating-ui/react-dom@"^2.0.8" from @vkontakte/vkui-floating-ui@0.1.5
npm error node_modules/@vkontakte/vkui-floating-ui
npm error @vkontakte/vkui-floating-ui@"^0.1.2" from @vkontakte/vkui@6.0.2
npm error node_modules/@vkontakte/vkui
npm error @vkontakte/vkui@"6.0.2" from the root project
npm error 14 more (@monaco-editor/react, @uiw/react-codemirror, ...)
npm error
npm error Could not resolve dependency:
npm error peer react@"^17.0.0" from @happysanta/router@0.3.1
npm error node_modules/@happysanta/router
npm error @happysanta/router@"0.3.1" from the root project
npm error
npm error Conflicting peer dependency: react@17.0.2
npm error node_modules/react
npm error peer react@"^17.0.0" from @happysanta/router@0.3.1
npm error node_modules/@happysanta/router
npm error @happysanta/router@"0.3.1" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with --force or --legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.
npm error
npm error
npm error For a full report see:
npm error /home/hive/.npm/_logs/2025-09-10T17_59_42_363Z-eresolve-report.txt
npm error A complete log of this run can be found in: /home/hive/.npm/_logs/2025-09-10T17_59_42_363Z-debug-0.log
3 changes: 3 additions & 0 deletions GPTutor-Frontend/npm_install_legacy.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
npm warn deprecated @types/classnames@2.3.1: This is a stub types definition. classnames provides its own type definitions, so you do not need this installed.
npm warn deprecated @braintree/sanitize-url@3.1.0: Potential XSS vulnerability patched in v6.0.0.
npm warn deprecated axios@0.18.1: Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410
2 changes: 2 additions & 0 deletions GPTutor-Frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import ApplicationInfoHumor from "./modals/ApplicationInfoHumor/ApplicationInfoH
import { BingPanel } from "$/panels/BingPanel";
import VKDocQuestionPanel from "./panels/VKDocQuestionPanel/VKDocQestionPanel";
import { VkDocQuestionRequest } from "$/panels/VkDocQuestionRequest";
import { MarkdownReader } from "$/panels/MarkdownReader";
import { retrieveLaunchParams } from "@telegram-apps/sdk";

const App = () => {
Expand Down Expand Up @@ -198,6 +199,7 @@ const App = () => {
<BingPanel id={Panels.bingPanel} />
<VKDocQuestionPanel id={Panels.vkDocQuestionPanel} />
<VkDocQuestionRequest id={Panels.vkDocQuestionRequest} />
<MarkdownReader id={Panels.markdownReader} />
</View>
)}
</SplitLayout>
Expand Down
6 changes: 6 additions & 0 deletions GPTutor-Frontend/src/NavigationContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export type NavigationContextType = {
goToAnecdoteMain: () => void;
goToBingPanel: () => void;
goToVkDocQuestionRequest: () => void;
goToMarkdownReader: () => void;
openAlert: (data: AlertType) => void;
alert: AlertType;
isForbidden: boolean;
Expand Down Expand Up @@ -223,6 +224,10 @@ export function NavigationContextProvider({
router.pushPage(RoutingPages.vkDocQuestionRequest);
};

const goToMarkdownReader = () => {
router.pushPage(RoutingPages.markdownReader);
};

return (
<NavigationContext.Provider
value={{
Expand Down Expand Up @@ -266,6 +271,7 @@ export function NavigationContextProvider({
goToAnecdoteMain,
goToVkDocQuestionRequest,
goToBingPanel,
goToMarkdownReader,
alert,
isForbidden,
}}
Expand Down
4 changes: 4 additions & 0 deletions GPTutor-Frontend/src/entity/routing/routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export enum RoutingPages {
vkDocQuestionPanel = "/vk-doc-question-panel",

vkDocQuestionRequest = "/vk-doc-question-request",

markdownReader = "/markdown-reader",
}

export enum Views {
Expand Down Expand Up @@ -89,6 +91,8 @@ export enum Panels {
vkDocQuestionPanel = "vk-doc-question-panel",

vkDocQuestionRequest = "vk-doc-question-request",

markdownReader = "markdown-reader",
}

export enum Modals {
Expand Down
4 changes: 4 additions & 0 deletions GPTutor-Frontend/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ const routes = {
Panels.vkDocQuestionRequest,
Views.viewMain
),
[RoutingPages.markdownReader]: new Page(
Panels.markdownReader,
Views.viewMain
),
};

const router = new Router(routes);
Expand Down
110 changes: 110 additions & 0 deletions GPTutor-Frontend/src/panels/MarkdownReader/MarkdownReader.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
.container {
padding: 16px;
display: flex;
flex-direction: column;
gap: 16px;
height: calc(100vh - 120px);
overflow-y: auto;
}

.markdownContent {
flex: 1;
background: var(--vkui--color_background_content);
border-radius: 8px;
padding: 16px;
border: 1px solid var(--vkui--color_separator_primary);
overflow-y: auto;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
}

.markdownContent h1,
.markdownContent h2,
.markdownContent h3,
.markdownContent h4,
.markdownContent h5,
.markdownContent h6 {
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}

.markdownContent h1 {
font-size: 2em;
border-bottom: 1px solid var(--vkui--color_separator_primary);
padding-bottom: 0.3em;
}

.markdownContent h2 {
font-size: 1.5em;
border-bottom: 1px solid var(--vkui--color_separator_primary);
padding-bottom: 0.3em;
}

.markdownContent h3 {
font-size: 1.25em;
}

.markdownContent p {
margin-bottom: 16px;
}

.markdownContent code {
background: var(--vkui--color_background_secondary);
padding: 2px 4px;
border-radius: 3px;
font-size: 0.9em;
}

.markdownContent pre {
background: var(--vkui--color_background_secondary);
padding: 16px;
border-radius: 6px;
overflow-x: auto;
margin: 16px 0;
}

.markdownContent blockquote {
border-left: 4px solid var(--vkui--color_accent);
margin: 16px 0;
padding: 0 16px;
color: var(--vkui--color_text_secondary);
}

.markdownContent ul,
.markdownContent ol {
padding-left: 2em;
margin: 16px 0;
}

.markdownContent li {
margin: 0.25em 0;
}

.markdownContent a {
color: var(--vkui--color_accent);
text-decoration: none;
}

.markdownContent a:hover {
text-decoration: underline;
}

.markdownContent table {
border-collapse: collapse;
width: 100%;
margin: 16px 0;
}

.markdownContent th,
.markdownContent td {
border: 1px solid var(--vkui--color_separator_primary);
padding: 8px 12px;
text-align: left;
}

.markdownContent th {
background: var(--vkui--color_background_secondary);
font-weight: 600;
}
153 changes: 153 additions & 0 deletions GPTutor-Frontend/src/panels/MarkdownReader/MarkdownReader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import React, { useState, useEffect, useMemo } from "react";

import { AppPanelHeader } from "$/components/AppPanelHeader";
import { Panel, PanelHeaderBack, Button, File, Div, Input, Card, Paragraph, Text } from "@vkontakte/vkui";
import { AppContainer } from "$/components/AppContainer";
import { useNavigationContext } from "$/NavigationContext";
import { FullscreenButton } from "$/components/FullscreenButton";
import Markdown from "$/services/Markdown";

import classes from "./MarkdownReader.module.css";

interface IProps {
id: string;
}

function MarkdownReader({ id }: IProps) {
const { goBack } = useNavigationContext();
const [markdownContent, setMarkdownContent] = useState<string>("");
const [fileName, setFileName] = useState<string>("");
const [urlInput, setUrlInput] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(false);

const markdown = useMemo(() => new Markdown(), []);
const html = markdownContent ? markdown.render(markdownContent) : "";

const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file && file.type === "text/markdown") {
setFileName(file.name);
const reader = new FileReader();
reader.onload = (e) => {
const content = e.target?.result as string;
setMarkdownContent(content);
};
reader.readAsText(file);
}
};

const handleUrlLoad = async () => {
if (!urlInput.trim()) return;

setIsLoading(true);
try {
const response = await fetch(urlInput);
if (response.ok) {
const content = await response.text();
setMarkdownContent(content);
setFileName(urlInput.split('/').pop() || 'loaded-file.md');
} else {
console.error('Failed to load markdown file from URL');
}
} catch (error) {
console.error('Error loading markdown file:', error);
} finally {
setIsLoading(false);
}
};

const sampleMarkdown = `# Добро пожаловать в Markdown Reader

## Что это такое?

Это средство для чтения и просмотра Markdown файлов с поддержкой:

- **Выделения кода** с подсветкой синтаксиса
- Математических формул с MathJax
- Сносок и ссылок
- И многого другого!

## Пример кода

\`\`\`javascript
function hello() {
console.log("Привет, мир!");
}
\`\`\`

## Математика

Inline формула: $E = mc^2$

Block формула:
$$\\sum_{i=1}^{n} x_i = x_1 + x_2 + \\cdots + x_n$$

> Это цитата с важной информацией

Используйте загрузку файла или URL для просмотра ваших Markdown документов!`;

useEffect(() => {
if (!markdownContent) {
setMarkdownContent(sampleMarkdown);
setFileName("sample.md");
}
}, []);

return (
<Panel id={id}>
<AppContainer
withoutTabbar
headerChildren={
<AppPanelHeader
before={<PanelHeaderBack onClick={goBack} />}
after={<FullscreenButton />}
>
Markdown Reader
</AppPanelHeader>
}
>
<div className={classes.container}>
<Card>
<Div>
<Text weight="2">Загрузить Markdown файл</Text>
<input
type="file"
accept=".md,.markdown"
onChange={handleFileUpload}
style={{ marginTop: 12, marginBottom: 12 }}
/>

<Input
placeholder="Или введите URL к .md файлу"
value={urlInput}
onChange={(e) => setUrlInput(e.target.value)}
style={{ marginBottom: 12 }}
/>

<Button
onClick={handleUrlLoad}
disabled={!urlInput.trim() || isLoading}
size="s"
mode="secondary"
>
{isLoading ? "Загрузка..." : "Загрузить по URL"}
</Button>

{fileName && (
<Paragraph style={{ marginTop: 12 }}>
<Text weight="2">Текущий файл:</Text> {fileName}
</Paragraph>
)}
</Div>
</Card>

<div className={classes.markdownContent}>
<div dangerouslySetInnerHTML={{ __html: html }} />
</div>
</div>
</AppContainer>
</Panel>
);
}

export default MarkdownReader;
Loading