├── .devcontainer ├── Dockerfile ├── devcontainer.json └── docker-compose.yml ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ └── codeql.yml ├── .gitignore ├── .gitmodules ├── .npmrc ├── .nycrc.yml ├── .vscode ├── extensions.json └── settings.json ├── .yarn └── releases │ └── yarn-4.9.1.cjs ├── .yarnrc.yml ├── CITATION.cff ├── CLA.md ├── CONTRIBUTING.md ├── LICENSE ├── README-EN.md ├── README.md ├── build ├── clearCache.js ├── gen-patch.ts ├── prepare.js ├── publish.ts ├── utils.ts └── version.ts ├── eslint.config.mjs ├── examples ├── reverse_proxy │ ├── Caddyfile │ ├── h2o.conf │ └── nginx.conf └── testdata │ ├── A+B Problem │ ├── aplusb1.in │ └── aplusb1.out │ └── Special Judge │ ├── a1.in │ ├── a1.out │ ├── chk.cc │ └── config.yaml ├── flake.lock ├── flake.nix ├── framework ├── eslint-config │ ├── base.mjs │ ├── build.ts │ └── package.json ├── framework │ ├── api.ts │ ├── base.ts │ ├── decorators.ts │ ├── error.ts │ ├── index.ts │ ├── interface.ts │ ├── package.json │ ├── router.ts │ ├── serializer.ts │ ├── server.ts │ ├── tests │ │ ├── projection.spec.ts │ │ └── validator.spec.ts │ └── validator.ts └── register │ ├── index.js │ └── package.json ├── install ├── docker │ ├── README.md │ ├── backend │ │ ├── Dockerfile │ │ └── entrypoint.sh │ ├── docker-compose.yml │ └── judge │ │ ├── Dockerfile │ │ ├── entrypoint.sh │ │ └── judge.yaml ├── helm-single │ ├── .helmignore │ ├── Chart.yaml │ ├── readme.md │ ├── templates │ │ ├── backend.config.yaml │ │ ├── backend.deployment.yaml │ │ ├── backend.service.yaml │ │ ├── ingress.yaml │ │ ├── judge.config.yaml │ │ ├── judge.deployment.yaml │ │ ├── mongo.deployment.yaml │ │ ├── mongo.service.yaml │ │ └── namespace.yaml │ └── values.yaml ├── install.sh ├── install.ts ├── nix │ ├── default.nix │ ├── env.nix │ └── hydro.nix ├── reset.sh └── uninstall.sh ├── package.json ├── packages ├── a11y │ ├── index.ts │ ├── package.json │ └── performance-test.ts ├── blog │ ├── index.ts │ ├── package.json │ └── templates │ │ ├── blog_detail.html │ │ ├── blog_edit.html │ │ └── blog_main.html ├── center │ ├── index.ts │ └── package.json ├── common │ ├── cases.ts │ ├── index.ts │ ├── lang.ts │ ├── package.json │ ├── status.ts │ ├── subtask.ts │ ├── tests │ │ └── subtask.spec.ts │ └── types.ts ├── components │ ├── frontend │ │ ├── ConfigEditor.tsx │ │ ├── Dom.tsx │ │ ├── Icon.tsx │ │ ├── Layout.tsx │ │ ├── Notification.tsx │ │ ├── autocomplete │ │ │ ├── AutoComplete.tsx │ │ │ ├── CustomSelectAutoComplete.tsx │ │ │ └── autocomplete.scss │ │ ├── common.scss │ │ ├── index.ts │ │ ├── provider.tsx │ │ └── types.d.ts │ └── package.json ├── elastic │ ├── index.ts │ └── package.json ├── fps-importer │ ├── index.ts │ ├── package.json │ └── template │ │ └── problem_import_fps.html ├── geoip │ ├── README.md │ ├── download.sh │ ├── index.ts │ ├── locale │ │ ├── zh.yaml │ │ └── zh_TW.yaml │ └── package.json ├── hydrojudge │ ├── .npmignore │ ├── bin │ │ └── hydrojudge.js │ ├── langs.yaml │ ├── locales │ │ ├── ko.yaml │ │ └── zh.yaml │ ├── package.json │ └── src │ │ ├── cache.ts │ │ ├── cases.ts │ │ ├── checkers.ts │ │ ├── compile.ts │ │ ├── config.ts │ │ ├── daemon.ts │ │ ├── error.ts │ │ ├── flow.ts │ │ ├── hosts │ │ ├── builtin.ts │ │ ├── hydro.ts │ │ └── vj4.ts │ │ ├── index.ts │ │ ├── info.ts │ │ ├── interface.ts │ │ ├── judge │ │ ├── communication.ts │ │ ├── default.ts │ │ ├── generate.ts │ │ ├── hack.ts │ │ ├── index.ts │ │ ├── interactive.ts │ │ ├── interface.ts │ │ ├── objective.ts │ │ ├── run.ts │ │ └── submit_answer.ts │ │ ├── log.ts │ │ ├── sandbox.ts │ │ ├── sandbox │ │ ├── client.ts │ │ └── interface.ts │ │ ├── signals.ts │ │ ├── task.ts │ │ ├── terminal.ts │ │ ├── testlib.ts │ │ └── utils.ts ├── hydrooj │ ├── README.md │ ├── bin │ │ ├── commands.ts │ │ └── hydrooj.js │ ├── locales │ │ ├── en.yaml │ │ ├── ko.yaml │ │ ├── zh.yaml │ │ └── zh_TW.yaml │ ├── package.json │ ├── setting.yaml │ └── src │ │ ├── commands │ │ ├── addon.ts │ │ ├── db.ts │ │ ├── install.ts │ │ └── patch.ts │ │ ├── context.ts │ │ ├── entry │ │ ├── cli.ts │ │ ├── common.ts │ │ ├── setup.ts │ │ └── worker.ts │ │ ├── error.ts │ │ ├── handler │ │ ├── compat.ts │ │ ├── contest.ts │ │ ├── discussion.ts │ │ ├── domain.ts │ │ ├── home.ts │ │ ├── homework.ts │ │ ├── import.ts │ │ ├── judge.ts │ │ ├── manage.ts │ │ ├── misc.ts │ │ ├── problem.ts │ │ ├── record.ts │ │ ├── status.ts │ │ ├── training.ts │ │ └── user.ts │ │ ├── init.ts │ │ ├── interface.ts │ │ ├── lib │ │ ├── avatar.ts │ │ ├── content.ts │ │ ├── difficulty.ts │ │ ├── hash.hydro.ts │ │ ├── i18n.ts │ │ ├── index.ts │ │ ├── mail.ts │ │ ├── mime.ts │ │ ├── rating.ts │ │ ├── testdataConfig.ts │ │ ├── ui.ts │ │ └── verifyTFA.ts │ │ ├── libs.ts │ │ ├── loader.ts │ │ ├── logger.ts │ │ ├── model │ │ ├── blacklist.ts │ │ ├── builtin.ts │ │ ├── contest.ts │ │ ├── discussion.ts │ │ ├── document.ts │ │ ├── domain.ts │ │ ├── message.ts │ │ ├── oauth.ts │ │ ├── opcount.ts │ │ ├── oplog.ts │ │ ├── problem.ts │ │ ├── record.ts │ │ ├── schedule.ts │ │ ├── setting.ts │ │ ├── solution.ts │ │ ├── storage.ts │ │ ├── system.ts │ │ ├── task.ts │ │ ├── token.ts │ │ ├── training.ts │ │ └── user.ts │ │ ├── options.ts │ │ ├── pipelineUtils.ts │ │ ├── plugin-api.ts │ │ ├── script │ │ ├── blacklist.ts │ │ ├── checkUpdate.ts │ │ ├── deleteUser.ts │ │ ├── fixStorage.ts │ │ ├── problemStat.ts │ │ ├── rating.ts │ │ └── storageUsage.ts │ │ ├── service │ │ ├── bus.ts │ │ ├── check.ts │ │ ├── db.ts │ │ ├── hmr.ts │ │ ├── layers │ │ │ ├── base.ts │ │ │ ├── domain.ts │ │ │ └── user.ts │ │ ├── migration.ts │ │ ├── monitor.ts │ │ ├── server.ts │ │ ├── storage.ts │ │ ├── watch.ts │ │ └── worker.ts │ │ ├── settings.ts │ │ ├── typeutils.ts │ │ ├── ui.ts │ │ ├── upgrade.ts │ │ ├── utils.ts │ │ └── welcome.ts ├── import-hoj │ ├── index.ts │ └── package.json ├── import-qduoj │ ├── index.ts │ └── package.json ├── login-with-github │ ├── README.md │ ├── index.ts │ └── package.json ├── login-with-google │ ├── README.md │ ├── index.ts │ └── package.json ├── migrate │ ├── README.md │ ├── index.ts │ ├── package.json │ └── scripts │ │ ├── hustoj.ts │ │ ├── poj.ts │ │ ├── syzoj.ts │ │ ├── universaloj.ts │ │ └── vijos.ts ├── onlyoffice │ ├── frontend │ │ └── office.page.ts │ ├── index.ts │ └── package.json ├── onsite-toolkit │ ├── frontend │ │ └── resolver.page.tsx │ ├── index.ts │ ├── interface.ts │ ├── package.json │ ├── public │ │ ├── logo.png │ │ └── resolver.css │ └── templates │ │ └── resolver.html ├── prom-client │ ├── index.ts │ ├── metrics.ts │ └── package.json ├── scoreboard-xcpcio │ ├── .npmignore │ ├── index.ts │ ├── install.ts │ ├── package.json │ └── templates │ │ └── xcpcio_board.html ├── sonic │ ├── index.ts │ └── package.json ├── telegram │ ├── index.ts │ ├── package.json │ └── templates │ │ └── telegram_login.html ├── ui-default │ ├── .npmignore │ ├── README.md │ ├── api.ts │ ├── backendlib │ │ ├── builder.ts │ │ ├── markdown-it-imsize.ts │ │ ├── markdown-it-katex.ts │ │ ├── markdown-it-media.ts │ │ ├── markdown-it-xss.ts │ │ ├── markdown.js │ │ ├── misc.ts │ │ └── template.ts │ ├── breakpoints.json │ ├── build │ │ ├── config │ │ │ └── webpack.ts │ │ ├── index.js │ │ ├── main.ts │ │ └── utils │ │ │ └── root.ts │ ├── common │ │ ├── color.inc.styl │ │ ├── common.inc.styl │ │ ├── easing.inc.styl │ │ ├── functions.inc.styl │ │ ├── rem.inc.styl │ │ └── variables.inc.styl │ ├── components │ │ ├── DOMAttachedObject.ts │ │ ├── autocomplete │ │ │ ├── CustomSelectAutoComplete.tsx │ │ │ ├── DomainSelectAutoComplete.tsx │ │ │ ├── FileSelectAutoComplete.tsx │ │ │ ├── ProblemSelectAutoComplete.tsx │ │ │ ├── UserSelectAutoComplete.tsx │ │ │ ├── components │ │ │ │ ├── DomainSelectAutoComplete.tsx │ │ │ │ ├── FileSelectAutoComplete.tsx │ │ │ │ ├── ProblemSelectAutoComplete.tsx │ │ │ │ └── UserSelectAutoComplete.tsx │ │ │ ├── domainselectautocomplete.page.styl │ │ │ ├── index.tsx │ │ │ ├── problemselectautocomplete.page.styl │ │ │ └── userselectautocomplete.page.styl │ │ ├── browser-update.page.ts │ │ ├── calendar │ │ │ ├── calendar.page.styl │ │ │ └── index.js │ │ ├── clipboard.page.ts │ │ ├── contest │ │ │ ├── contest.page.styl │ │ │ ├── contest.page.ts │ │ │ ├── contest_sidebar.page.styl │ │ │ ├── problem-contest-bg.png │ │ │ └── problem-contest-bg@2x.png │ │ ├── customFont.page.tsx │ │ ├── datepicker │ │ │ ├── datepicker.page.tsx │ │ │ ├── datepicker.styl │ │ │ └── timepicker.ts │ │ ├── dialog │ │ │ ├── DomDialog.ts │ │ │ ├── dialog.page.styl │ │ │ └── index.tsx │ │ ├── discussion │ │ │ ├── CommentBox.js │ │ │ ├── comments.page.styl │ │ │ ├── comments.page.tsx │ │ │ ├── discussion.page.styl │ │ │ ├── history.page.tsx │ │ │ ├── reaction.page.styl │ │ │ └── reaction.page.tsx │ │ ├── drop │ │ │ └── drop.page.styl │ │ ├── dropdown │ │ │ ├── Dropdown.js │ │ │ ├── dropdown.page.styl │ │ │ └── dropdown.page.ts │ │ ├── editor │ │ │ ├── cmeditor.page.ts │ │ │ ├── cmeditor.styl │ │ │ ├── index.tsx │ │ │ ├── mdeditor.ts │ │ │ └── textareaHandler.ts │ │ ├── footer │ │ │ ├── footer.page.styl │ │ │ └── footer.page.ts │ │ ├── form │ │ │ ├── button.page.default.styl │ │ │ ├── button.page.styl │ │ │ ├── checkbox.page.styl │ │ │ ├── form.page.styl │ │ │ ├── form.page.ts │ │ │ ├── multiSelectCheckbox.page.js │ │ │ ├── radiobox.page.styl │ │ │ ├── select.page.styl │ │ │ ├── textbox.page.styl │ │ │ ├── textbox.page.ts │ │ │ └── var.inc.styl │ │ ├── header │ │ │ └── header.page.styl │ │ ├── highlighter │ │ │ ├── code-example.js │ │ │ ├── highlighter.page.styl │ │ │ ├── highlighter.page.ts │ │ │ ├── meta.js │ │ │ └── prismjs.js │ │ ├── hint.page.styl │ │ ├── hint.ts │ │ ├── hitokoto │ │ │ └── index.page.js │ │ ├── hotkey │ │ │ └── hotkey.page.js │ │ ├── katex │ │ │ ├── katex.page.js │ │ │ └── katex.page.styl │ │ ├── languageselect.tsx │ │ ├── loader │ │ │ └── loader.page.styl │ │ ├── marker │ │ │ ├── Marker.js │ │ │ ├── MarkerReactive.js │ │ │ ├── marker.page.js │ │ │ └── marker.page.styl │ │ ├── media │ │ │ └── media.page.js │ │ ├── menu │ │ │ ├── menu-heading.page.js │ │ │ ├── menu.page.js │ │ │ └── menu.page.styl │ │ ├── message │ │ │ ├── index.page.ts │ │ │ └── worker.ts │ │ ├── messagepad │ │ │ ├── DialogueListItem.page.styl │ │ │ ├── DialogueListItemComponent.jsx │ │ │ ├── Message.page.styl │ │ │ ├── MessageComponent.jsx │ │ │ ├── MessagePad.page.styl │ │ │ ├── MessagePadDialogueContentContainer.jsx │ │ │ ├── MessagePadDialogueListContainer.jsx │ │ │ ├── MessagePadInputContainer.jsx │ │ │ ├── index.jsx │ │ │ └── reducers │ │ │ │ ├── activeId.ts │ │ │ │ ├── dialogues.js │ │ │ │ ├── index.ts │ │ │ │ ├── inputs.js │ │ │ │ └── isPosting.ts │ │ ├── monaco │ │ │ ├── index.ts │ │ │ ├── languages │ │ │ │ ├── markdown.ts │ │ │ │ ├── typescript.ts │ │ │ │ └── yaml.ts │ │ │ ├── loader.ts │ │ │ ├── monaco.styl │ │ │ ├── nls.js │ │ │ └── schema │ │ │ │ └── problemconfig.ts │ │ ├── navigation │ │ │ ├── hamburgers.page.styl │ │ │ ├── nav-logo-small_dark.png │ │ │ ├── navigation.page.js │ │ │ └── navigation.page.styl │ │ ├── notification │ │ │ ├── index.ts │ │ │ ├── notification.page.js │ │ │ └── notification.page.styl │ │ ├── nprogress.page.styl │ │ ├── omnisearch │ │ │ ├── index.page.tsx │ │ │ └── omnibar.page.styl │ │ ├── pager │ │ │ └── pager.page.styl │ │ ├── preview │ │ │ └── preview.page.ts │ │ ├── problem │ │ │ ├── create.page.js │ │ │ ├── list.page.js │ │ │ ├── rp.page.styl │ │ │ └── tag.page.styl │ │ ├── problemconfig │ │ │ ├── BasicForm.tsx │ │ │ ├── ProblemConfigEditor.tsx │ │ │ ├── ProblemConfigForm.tsx │ │ │ ├── ProblemConfigTree.tsx │ │ │ ├── ProblemType.tsx │ │ │ ├── index.tsx │ │ │ ├── reducer │ │ │ │ ├── config.ts │ │ │ │ ├── index.ts │ │ │ │ └── testdata.ts │ │ │ └── tree │ │ │ │ ├── AddTestcase.tsx │ │ │ │ ├── SelectionManager.tsx │ │ │ │ ├── SubtaskSettings.tsx │ │ │ │ └── Testcase.tsx │ │ ├── profile │ │ │ ├── backgrounds │ │ │ │ ├── 1.jpg │ │ │ │ ├── 10.jpg │ │ │ │ ├── 11.jpg │ │ │ │ ├── 12.jpg │ │ │ │ ├── 13.jpg │ │ │ │ ├── 14.jpg │ │ │ │ ├── 15.jpg │ │ │ │ ├── 16.jpg │ │ │ │ ├── 17.jpg │ │ │ │ ├── 18.jpg │ │ │ │ ├── 19.jpg │ │ │ │ ├── 2.jpg │ │ │ │ ├── 20.jpg │ │ │ │ ├── 21.jpg │ │ │ │ ├── 3.jpg │ │ │ │ ├── 4.jpg │ │ │ │ ├── 5.jpg │ │ │ │ ├── 6.jpg │ │ │ │ ├── 7.jpg │ │ │ │ ├── 8.jpg │ │ │ │ ├── 9.jpg │ │ │ │ ├── gen_thumbnails.sh │ │ │ │ └── thumbnail │ │ │ │ │ ├── 1.jpg │ │ │ │ │ ├── 10.jpg │ │ │ │ │ ├── 11.jpg │ │ │ │ │ ├── 12.jpg │ │ │ │ │ ├── 13.jpg │ │ │ │ │ ├── 14.jpg │ │ │ │ │ ├── 15.jpg │ │ │ │ │ ├── 16.jpg │ │ │ │ │ ├── 17.jpg │ │ │ │ │ ├── 18.jpg │ │ │ │ │ ├── 19.jpg │ │ │ │ │ ├── 2.jpg │ │ │ │ │ ├── 20.jpg │ │ │ │ │ ├── 21.jpg │ │ │ │ │ ├── 3.jpg │ │ │ │ │ ├── 4.jpg │ │ │ │ │ ├── 5.jpg │ │ │ │ │ ├── 6.jpg │ │ │ │ │ ├── 7.jpg │ │ │ │ │ ├── 8.jpg │ │ │ │ │ └── 9.jpg │ │ │ └── profile.page.styl │ │ ├── react │ │ │ ├── DomComponent.tsx │ │ │ └── IconComponent.tsx │ │ ├── record │ │ │ └── record.page.styl │ │ ├── rotator │ │ │ ├── index.js │ │ │ └── rotator.page.styl │ │ ├── scratchpad │ │ │ ├── DataInput.page.styl │ │ │ ├── DataInputComponent.jsx │ │ │ ├── Editor.page.styl │ │ │ ├── Panel.page.styl │ │ │ ├── PanelButton.page.styl │ │ │ ├── PanelButtonComponent.jsx │ │ │ ├── PanelComponent.jsx │ │ │ ├── ScratchpadEditorContainer.tsx │ │ │ ├── ScratchpadPretestContainer.jsx │ │ │ ├── ScratchpadRecordsContainer.jsx │ │ │ ├── ScratchpadRecordsRowContainer.jsx │ │ │ ├── ScratchpadSettings.tsx │ │ │ ├── ScratchpadToolbarContainer.jsx │ │ │ ├── Tab.page.styl │ │ │ ├── Toolbar.page.styl │ │ │ ├── ToolbarComponent.jsx │ │ │ ├── index.tsx │ │ │ ├── reducers │ │ │ │ ├── editor.ts │ │ │ │ ├── index.ts │ │ │ │ ├── pretest.ts │ │ │ │ ├── records.ts │ │ │ │ ├── state.ts │ │ │ │ └── ui.ts │ │ │ ├── scratchpad.page.styl │ │ │ └── var.inc.styl │ │ ├── selectUser.tsx │ │ ├── signin │ │ │ ├── signInDialog.page.js │ │ │ └── signin_dialog.page.styl │ │ ├── smoothscroll │ │ │ └── smoothscroll.page.js │ │ ├── socket │ │ │ └── index.ts │ │ ├── star │ │ │ ├── star.page.styl │ │ │ └── star.page.ts │ │ ├── sticky │ │ │ └── sticky.page.js │ │ ├── tab │ │ │ ├── Tab.js │ │ │ ├── tab.page.js │ │ │ ├── tab.page.styl │ │ │ └── var.inc.styl │ │ ├── table │ │ │ ├── StyledTable.js │ │ │ ├── styledTable.page.js │ │ │ └── table.page.styl │ │ ├── time │ │ │ └── time.page.js │ │ ├── tooltip │ │ │ ├── Tooltip.js │ │ │ ├── tooltip.page.js │ │ │ └── tooltip.page.styl │ │ ├── training │ │ │ └── training.page.styl │ │ ├── upload.ts │ │ ├── vote │ │ │ └── vote.page.js │ │ └── zipDownloader │ │ │ └── index.ts │ ├── constant │ │ ├── domain.js │ │ ├── message.js │ │ └── record.js │ ├── context.ts │ ├── entry.js │ ├── hydro.ts │ ├── index.ts │ ├── lazyload.ts │ ├── locales │ │ ├── en.yaml │ │ ├── ko.yaml │ │ ├── zh.yaml │ │ └── zh_TW.yaml │ ├── misc │ │ ├── Page.ts │ │ ├── PageLoader.js │ │ ├── float.styl │ │ ├── grid.styl │ │ ├── icons │ │ │ ├── account--circle.svg │ │ │ ├── add.svg │ │ │ ├── award.svg │ │ │ ├── balloon.svg │ │ │ ├── block.svg │ │ │ ├── bold.svg │ │ │ ├── book.svg │ │ │ ├── calendar.svg │ │ │ ├── check--circle.svg │ │ │ ├── check.svg │ │ │ ├── chevron_left.svg │ │ │ ├── chevron_right.svg │ │ │ ├── close--circle.svg │ │ │ ├── close.svg │ │ │ ├── code.svg │ │ │ ├── comment--multiple.svg │ │ │ ├── comment--text.svg │ │ │ ├── comparison.svg │ │ │ ├── copy.svg │ │ │ ├── crown.svg │ │ │ ├── debug.svg │ │ │ ├── delete.svg │ │ │ ├── download.svg │ │ │ ├── edit.svg │ │ │ ├── emoji.svg │ │ │ ├── enlarge.svg │ │ │ ├── erase.svg │ │ │ ├── expand_less.svg │ │ │ ├── expand_more.svg │ │ │ ├── facebook.svg │ │ │ ├── feeling-lucky.svg │ │ │ ├── file.svg │ │ │ ├── filter.svg │ │ │ ├── fingerprint.svg │ │ │ ├── flag.svg │ │ │ ├── formula.svg │ │ │ ├── github.svg │ │ │ ├── global.svg │ │ │ ├── google_plus.svg │ │ │ ├── heart--outline.svg │ │ │ ├── heart.svg │ │ │ ├── help.svg │ │ │ ├── help2.svg │ │ │ ├── homework.svg │ │ │ ├── hourglass.svg │ │ │ ├── info--circle.svg │ │ │ ├── info.svg │ │ │ ├── insert--image.svg │ │ │ ├── insert--link.svg │ │ │ ├── italic.svg │ │ │ ├── lab.svg │ │ │ ├── link--external.svg │ │ │ ├── link.svg │ │ │ ├── linkedin.svg │ │ │ ├── logout.svg │ │ │ ├── mail.svg │ │ │ ├── ordered_list.svg │ │ │ ├── pie-chart.svg │ │ │ ├── platform--android.svg │ │ │ ├── platform--chromeos.svg │ │ │ ├── platform--ios.svg │ │ │ ├── platform--linux.svg │ │ │ ├── platform--mac.svg │ │ │ ├── platform--mobile.svg │ │ │ ├── platform--unknown.svg │ │ │ ├── platform--windows.svg │ │ │ ├── play.svg │ │ │ ├── preview.svg │ │ │ ├── qq.svg │ │ │ ├── quote.svg │ │ │ ├── refresh.svg │ │ │ ├── reply.svg │ │ │ ├── schedule--fill.svg │ │ │ ├── schedule.svg │ │ │ ├── search.svg │ │ │ ├── security.svg │ │ │ ├── send.svg │ │ │ ├── settings.svg │ │ │ ├── shrink.svg │ │ │ ├── sliders.svg │ │ │ ├── stack.svg │ │ │ ├── star--outline.svg │ │ │ ├── star.svg │ │ │ ├── statistics.svg │ │ │ ├── stopwatch.svg │ │ │ ├── tag.svg │ │ │ ├── template │ │ │ │ ├── webicon.inc.styl │ │ │ │ └── webicon.styl │ │ │ ├── twitter.svg │ │ │ ├── unordered_list.svg │ │ │ ├── upload.svg │ │ │ ├── usb.svg │ │ │ ├── user--multiple.svg │ │ │ ├── user.svg │ │ │ ├── vote--down.svg │ │ │ ├── vote--up.svg │ │ │ ├── warning.svg │ │ │ ├── web.svg │ │ │ ├── wechat.svg │ │ │ └── wrench.svg │ │ ├── immersive-background.jpg │ │ ├── immersive-background@2x.jpg │ │ ├── immersive.styl │ │ ├── nothing.styl │ │ ├── puzzled_twd2.svg │ │ ├── section.styl │ │ ├── slideout.styl │ │ ├── structure.styl │ │ ├── textalign.styl │ │ └── typography.styl │ ├── package.json │ ├── pages │ │ ├── api.page.styl │ │ ├── contest.page.styl │ │ ├── contest.page.ts │ │ ├── contest_balloon.page.tsx │ │ ├── contest_edit.page.ts │ │ ├── contest_main.page.ts │ │ ├── contest_manage.page.ts │ │ ├── contest_scoreboard.page.ts │ │ ├── contest_user.page.ts │ │ ├── discussion_detail.page.styl │ │ ├── discussion_main.page.styl │ │ ├── discussion_node_bg │ │ │ ├── nodes@2x_advice.png │ │ │ ├── nodes@2x_qa.png │ │ │ ├── nodes@2x_share.png │ │ │ ├── nodes@2x_solution.png │ │ │ ├── nodes@2x_vijos.png │ │ │ ├── nodes_advice.png │ │ │ ├── nodes_qa.png │ │ │ ├── nodes_share.png │ │ │ ├── nodes_solution.png │ │ │ └── nodes_vijos.png │ │ ├── domain_edit.page.ts │ │ ├── domain_group.page.styl │ │ ├── domain_group.page.ts │ │ ├── domain_join.page.styl │ │ ├── domain_join_applications.page.ts │ │ ├── domain_permission.page.styl │ │ ├── domain_role.page.js │ │ ├── domain_role.page.styl │ │ ├── domain_user.page.js │ │ ├── domain_user.page.styl │ │ ├── error.page.styl │ │ ├── error │ │ │ └── twd2.svg │ │ ├── files.page.js │ │ ├── files.page.styl │ │ ├── home_account.page.styl │ │ ├── home_domain.page.styl │ │ ├── home_domain.page.tsx │ │ ├── home_messages.page.tsx │ │ ├── home_preference.page.jsx │ │ ├── home_security.page.styl │ │ ├── home_security.page.tsx │ │ ├── home_settings.page.ts │ │ ├── homework_detail.page.styl │ │ ├── homework_edit.page.ts │ │ ├── homework_main.page.js │ │ ├── homework_main.page.styl │ │ ├── homework_scoreboard.page.styl │ │ ├── manage_dashboard.page.js │ │ ├── manage_script.page.js │ │ ├── manage_user_import.page.js │ │ ├── manage_user_priv.page.js │ │ ├── manage_user_priv.page.styl │ │ ├── problem_config.page.styl │ │ ├── problem_config.page.tsx │ │ ├── problem_detail.page.styl │ │ ├── problem_detail.page.tsx │ │ ├── problem_edit.page.js │ │ ├── problem_edit.page.styl │ │ ├── problem_files.page.styl │ │ ├── problem_files.page.tsx │ │ ├── problem_main.page.styl │ │ ├── problem_main.page.ts │ │ ├── problem_sidebar.page.ts │ │ ├── problem_statistics.page.styl │ │ ├── problem_statistics.page.ts │ │ ├── problem_submit.page.styl │ │ ├── problem_submit.page.tsx │ │ ├── ranking.page.styl │ │ ├── record_detail.page.styl │ │ ├── record_detail.page.tsx │ │ ├── record_main.page.styl │ │ ├── record_main.page.ts │ │ ├── setting.page.tsx │ │ ├── status.page.styl │ │ ├── training_detail.page.styl │ │ ├── training_detail.page.ts │ │ ├── training_edit.page.ts │ │ ├── training_main.page.styl │ │ ├── user_detail.page.styl │ │ ├── user_sudo.page.ts │ │ └── user_verify.page.ts │ ├── polyfill.ts │ ├── postcss.config.js │ ├── sentry.ts │ ├── service-worker.ts │ ├── setting.yaml │ ├── static │ │ ├── android-chrome-192x192.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── browser-update.js │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── img │ │ │ ├── avatar.png │ │ │ └── team_avatar.png │ │ └── robots.txt │ ├── templates │ │ ├── about.html │ │ ├── bsod.html │ │ ├── components │ │ │ ├── comments_discussion.html │ │ │ ├── comments_solution.html │ │ │ ├── contest.html │ │ │ ├── form.html │ │ │ ├── home.html │ │ │ ├── homework.html │ │ │ ├── md_hint.html │ │ │ ├── noscript_note.html │ │ │ ├── nothing.html │ │ │ ├── paginator.html │ │ │ ├── problem.html │ │ │ ├── record.html │ │ │ ├── sidemenu.html │ │ │ └── user.html │ │ ├── contest_balloon.html │ │ ├── contest_detail.html │ │ ├── contest_edit.html │ │ ├── contest_main.html │ │ ├── contest_manage.html │ │ ├── contest_problemlist.html │ │ ├── contest_scoreboard.html │ │ ├── contest_scoreboard_download_html.html │ │ ├── contest_user.html │ │ ├── discussion_create.html │ │ ├── discussion_detail.html │ │ ├── discussion_edit.html │ │ ├── discussion_main_or_node.html │ │ ├── domain_base.html │ │ ├── domain_create.html │ │ ├── domain_dashboard.html │ │ ├── domain_edit.html │ │ ├── domain_group.html │ │ ├── domain_join.html │ │ ├── domain_join_applications.html │ │ ├── domain_permission.html │ │ ├── domain_role.html │ │ ├── domain_user.html │ │ ├── domain_user_raw.html │ │ ├── error.html │ │ ├── home_domain.html │ │ ├── home_files.html │ │ ├── home_messages.html │ │ ├── home_security.html │ │ ├── home_settings.html │ │ ├── homework_detail.html │ │ ├── homework_edit.html │ │ ├── homework_files.html │ │ ├── homework_main.html │ │ ├── layout │ │ │ ├── basic.html │ │ │ ├── home_base.html │ │ │ ├── html5.html │ │ │ ├── immersive.html │ │ │ ├── mail.html │ │ │ ├── simple.html │ │ │ └── wiki_base.html │ │ ├── main.html │ │ ├── manage_base.html │ │ ├── manage_config.html │ │ ├── manage_dashboard.html │ │ ├── manage_script.html │ │ ├── manage_setting.html │ │ ├── manage_user_import.html │ │ ├── manage_user_priv.html │ │ ├── partials │ │ │ ├── category.html │ │ │ ├── contest.html │ │ │ ├── contest_balloon.html │ │ │ ├── contest_sidebar.html │ │ │ ├── contest_sidebar_management.html │ │ │ ├── contest_user.html │ │ │ ├── discussion_edit_form.html │ │ │ ├── discussion_list.html │ │ │ ├── discussion_nodes_widget.html │ │ │ ├── files.html │ │ │ ├── footer.html │ │ │ ├── hamburger.html │ │ │ ├── header_mobile.html │ │ │ ├── homepage │ │ │ │ ├── bulletin.html │ │ │ │ ├── contest.html │ │ │ │ ├── discussion.html │ │ │ │ ├── discussion_nodes.html │ │ │ │ ├── error.html │ │ │ │ ├── hitokoto.html │ │ │ │ ├── homework.html │ │ │ │ ├── problem_search.html │ │ │ │ ├── ranking.html │ │ │ │ ├── recent_problems.html │ │ │ │ ├── starred_problems.html │ │ │ │ ├── suggestion.html │ │ │ │ └── training.html │ │ │ ├── homework.html │ │ │ ├── homework_default_penalty_rules.yaml │ │ │ ├── homework_sidebar.html │ │ │ ├── login_dialog.html │ │ │ ├── manage_user_priv.html │ │ │ ├── nav.html │ │ │ ├── problem-sidebar-information.html │ │ │ ├── problem.html │ │ │ ├── problem_category_dialog.html │ │ │ ├── problem_default.md │ │ │ ├── problem_description.html │ │ │ ├── problem_files.html │ │ │ ├── problem_list.html │ │ │ ├── problem_lucky.html │ │ │ ├── problem_sidebar.html │ │ │ ├── problem_sidebar_contest.html │ │ │ ├── problem_sidebar_homework.html │ │ │ ├── problem_sidebar_normal.html │ │ │ ├── problem_stat.html │ │ │ ├── scoreboard.html │ │ │ ├── setting.html │ │ │ ├── training_default.json │ │ │ ├── training_detail.html │ │ │ ├── user.html │ │ │ └── user_detail │ │ │ │ ├── activity.html │ │ │ │ └── solution.html │ │ ├── problem_config.html │ │ ├── problem_detail.html │ │ ├── problem_edit.html │ │ ├── problem_files.html │ │ ├── problem_hack.html │ │ ├── problem_import.html │ │ ├── problem_main.html │ │ ├── problem_solution.html │ │ ├── problem_statistics.html │ │ ├── problem_submit.html │ │ ├── ranking.html │ │ ├── record_detail.html │ │ ├── record_detail_status.html │ │ ├── record_detail_summary.html │ │ ├── record_main.html │ │ ├── record_main_tr.html │ │ ├── status.html │ │ ├── training_detail.html │ │ ├── training_edit.html │ │ ├── training_files.html │ │ ├── training_main.html │ │ ├── user_changemail_mail.html │ │ ├── user_changemail_mail_sent.html │ │ ├── user_delete_pending.html │ │ ├── user_detail.html │ │ ├── user_login.html │ │ ├── user_logout.html │ │ ├── user_lostpass.html │ │ ├── user_lostpass_mail.html │ │ ├── user_lostpass_mail_sent.html │ │ ├── user_lostpass_with_code.html │ │ ├── user_register.html │ │ ├── user_register_mail.html │ │ ├── user_register_mail_sent.html │ │ ├── user_register_with_code.html │ │ ├── user_sudo.html │ │ ├── user_sudo_redirect.html │ │ └── wiki_help.html │ ├── theme │ │ ├── dark.styl │ │ └── default.js │ ├── typed.d.ts │ └── utils │ │ ├── base.ts │ │ ├── base64.js │ │ ├── db.ts │ │ ├── index.ts │ │ ├── loadReactRedux.ts │ │ ├── mediaQuery.ts │ │ ├── pjax.js │ │ └── slide.ts ├── ui-next │ ├── index.html │ ├── index.ts │ ├── package.json │ └── src │ │ ├── App.tsx │ │ ├── main.tsx │ │ └── vite-env.d.ts ├── utils │ ├── lib │ │ ├── common.ts │ │ ├── locate-pm2.ts │ │ ├── register.js │ │ ├── search.ts │ │ ├── sysinfo.ts │ │ └── utils.ts │ └── package.json └── vjudge │ ├── package.json │ └── src │ ├── fetch.ts │ ├── index.ts │ ├── interface.ts │ ├── providers │ ├── codeforces.ts │ ├── csgoj.ts │ ├── hduoj.ts │ ├── hustoj.ts │ ├── index.ts │ ├── poj.ts │ ├── spoj.ts │ ├── uoj.ts │ └── yacs.ts │ ├── proxy.ts │ └── verdict.ts └── test ├── entry.js └── main.ts /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:20-bookworm 2 | ADD https://github.com/criyle/go-judge/releases/download/v1.8.0/go-judge_1.8.0_linux_amd64 /usr/bin/sandbox 3 | RUN npm install -g yarn && \ 4 | sudo apt-get update && sudo apt-get install gcc g++ && \ 5 | mkdir -p /root/.hydro /data/file && chmod +x /usr/bin/sandbox && \ 6 | chown -R root:root /root /root/.hydro /data/file && \ 7 | echo '{"uri":"mongodb://mongo/hydro"}' > /root/.hydro/config.json && \ 8 | echo '["@hydrooj/ui-default","@hydrooj/hydrojudge"]' > /root/.hydro/addon.json 9 | ENTRYPOINT sandbox 10 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Hydro Dev Container", 3 | "dockerComposeFile": "docker-compose.yml", 4 | "service": "hydro", 5 | "workspaceFolder": "/workspace", 6 | "customizations": { 7 | "vscode": { 8 | "extensions": [ 9 | "dbaeumer.vscode-eslint", 10 | "gruntfuggly.todo-tree", 11 | "ronnidc.nunjucks", 12 | "eamodio.gitlens", 13 | "vscode-icons-team.vscode-icons", 14 | "sysoev.language-stylus" 15 | ] 16 | } 17 | }, 18 | "remoteUser": "root", 19 | "forwardPorts": [ 20 | 2333, 21 | 8000 22 | ], 23 | "postCreateCommand": "git submodule update --init && yarn install && npx hydrooj cli system set server.port 2333 && npx hydrooj cli user create root@hydro.local root rootroot 2 && npx hydrooj cli user setSuperAdmin 2" 24 | } 25 | -------------------------------------------------------------------------------- /.devcontainer/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.9' 2 | 3 | services: 4 | mongo: 5 | image: mongo:4-focal 6 | restart: unless-stopped 7 | volumes: 8 | - mongodb-data:/data/db 9 | hydro: 10 | build: 11 | context: . 12 | dockerfile: Dockerfile 13 | privileged: true 14 | depends_on: 15 | - mongo 16 | volumes: 17 | - ..:/workspace:cached 18 | - testdata:/data/file 19 | 20 | volumes: 21 | mongodb-data: 22 | testdata: -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js text eol=lf 2 | *.jsx text eol=lf 3 | *.ts text eol=lf 4 | *.tsx text eol=lf 5 | *.json text eol=lf 6 | *.yaml text eol=lf 7 | *.yml text eol=lf 8 | *.md text eol=lf 9 | *.css text eol=lf 10 | *.scss text eol=lf 11 | *.less text eol=lf 12 | *.html text eol=lf 13 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [undefined-moe] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['https://pay.undefined.moe'] 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "testlib"] 2 | path = packages/hydrojudge/vendor/testlib 3 | url = https://github.com/MikeMirzayanov/testlib.git 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | strict-peer-dependencies=false 2 | resolution-mode=time-based 3 | -------------------------------------------------------------------------------- /.nycrc.yml: -------------------------------------------------------------------------------- 1 | extension: [.js, .ts, .jsx, .tsx] 2 | exclude: [node_modules, .yarn] 3 | reporter: [text, lcov] -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "gruntfuggly.todo-tree", 5 | "ronnidc.nunjucks", 6 | "sysoev.language-stylus" 7 | ] 8 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.autoGuessEncoding": false, 3 | "files.encoding": "utf8", 4 | "files.eol": "\n", 5 | "editor.detectIndentation": true, 6 | "typescript.tsdk": "node_modules/typescript/lib", 7 | "typescript.tsserver.maxTsServerMemory": 16384, 8 | "typescript.tsserver.nodePath": "node", 9 | "[json]": { 10 | "files.insertFinalNewline": true, 11 | "files.trimFinalNewlines": false 12 | }, 13 | "search.exclude": { 14 | "**/.yarn": true, 15 | "**/.pnp.*": true 16 | }, 17 | "files.associations": { 18 | "*.html": "nunjucks" 19 | } 20 | } -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | enableTelemetry: false 2 | 3 | nodeLinker: node-modules 4 | 5 | npmPublishAccess: public 6 | 7 | preferInteractive: true 8 | 9 | preferReuse: true 10 | 11 | tsEnableAutoTypes: true 12 | 13 | yarnPath: .yarn/releases/yarn-4.9.1.cjs 14 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | title: Hydro 3 | message: >- 4 | If you use this software, please cite it using the 5 | metadata from this file. 6 | type: software 7 | authors: 8 | - given-names: Yi 9 | family-names: Huang 10 | email: i@undefined.moe 11 | - given-names: Haojun 12 | family-names: Pan 13 | email: panda@hydro.ac 14 | - name: All Hydro Project Developers 15 | repository-code: 'https://github.com/hydro-dev/Hydro' 16 | url: 'https://hydro.js.org' 17 | keywords: 18 | - Online Judge 19 | - ICPC 20 | - OI 21 | - Competitive Programming 22 | license: AGPL-3.0-or-later 23 | -------------------------------------------------------------------------------- /build/clearCache.js: -------------------------------------------------------------------------------- 1 | const superagent = require('superagent'); 2 | const secret = process.env.GITHUB_TOKEN; 3 | async function main() { 4 | const res = await superagent.get('https://api.github.com/repos/hydro-dev/Hydro/actions/caches') 5 | .set('Accept', 'application/vnd.github+json') 6 | .set('User-Agent', 'Hydro') 7 | .set('Authorization', `Bearer ${secret}`); 8 | console.log(`Total ${res.body.total_count}`); 9 | console.log(res.body.actions_caches.map((i) => i.key)); 10 | await Promise.all(res.body.actions_caches.map((i) => superagent 11 | .delete(`https://api.github.com/repos/hydro-dev/Hydro/actions/caches?key=${i.key}`) 12 | .set('Accept', 'application/vnd.github+json') 13 | .set('User-Agent', 'Hydro') 14 | .set('Authorization', `Bearer ${secret}`))); 15 | } 16 | main(); 17 | -------------------------------------------------------------------------------- /build/gen-patch.ts: -------------------------------------------------------------------------------- 1 | import child from 'child_process'; 2 | import fs from 'fs'; 3 | import path from 'path'; 4 | import superagent from 'superagent'; 5 | 6 | if (!process.argv[2]) throw new Error('No target specified'); 7 | 8 | // TODO support module other than packages 9 | const target = process.argv[2].startsWith('packages/') ? process.argv[2] : `packages/${process.argv[2]}`; 10 | 11 | const result = child.execSync(`git diff ${target}`); 12 | const patch = result.toString().replace(new RegExp(`${target}/`, 'g'), ''); 13 | const filename = `${path.basename(target)}.patch`; 14 | fs.writeFileSync(filename, patch); 15 | superagent.post('https://hydro.ac/paste') 16 | .set('accept', 'application/json') 17 | .send({ body: patch, filename }) 18 | .end((err, res) => { 19 | if (err) throw err; 20 | console.log('Paste created on ', res.text); 21 | }); 22 | -------------------------------------------------------------------------------- /build/utils.ts: -------------------------------------------------------------------------------- 1 | import { SpawnOptions } from 'child_process'; 2 | import spawn from 'cross-spawn'; 3 | import { globby } from 'globby'; 4 | 5 | export const cwd = process.cwd(); 6 | 7 | export function getWorkspaces() { 8 | return globby(require('../package.json').workspaces, { 9 | cwd, 10 | deep: 0, 11 | onlyDirectories: true, 12 | expandDirectories: false, 13 | }); 14 | } 15 | 16 | export function spawnAsync(command, path) { 17 | const args = command.split(/\s+/); 18 | const options: SpawnOptions = { stdio: 'inherit' }; 19 | if (path) options.cwd = path; 20 | const child = spawn(args[0], args.slice(1), options); 21 | return new Promise((resolve, reject) => { 22 | child.on('close', resolve); 23 | child.on('error', reject); 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /examples/reverse_proxy/Caddyfile: -------------------------------------------------------------------------------- 1 | # 其他需求清参照 https://caddyserver.com/docs/ 说明进行设置。 2 | # For more information, refer to caddy v2 documentation. 3 | hydro.ac { 4 | encode zstd gzip 5 | log { 6 | output file /data/access.log { 7 | roll_size 1gb 8 | roll_keep_for 72h 9 | } 10 | format json 11 | } 12 | # Handle static files directly, for better performance. 13 | root * /root/.hydro/static 14 | @static { 15 | file { 16 | try_files {path} 17 | } 18 | } 19 | handle @static { 20 | file_server 21 | } 22 | handle { 23 | reverse_proxy http://127.0.0.1:8888 24 | } 25 | } 26 | 27 | www.hydro.ac { 28 | redir * https://hydro.ac{uri} 29 | } 30 | -------------------------------------------------------------------------------- /examples/reverse_proxy/h2o.conf: -------------------------------------------------------------------------------- 1 | listen: 2 | port: 8080 3 | hosts: 4 | "*": 5 | paths: 6 | /: 7 | proxy.reverse.url: "http://localhost:8888" 8 | proxy.preserve-host: ON 9 | proxy.websocket: ON -------------------------------------------------------------------------------- /examples/testdata/A+B Problem/aplusb1.in: -------------------------------------------------------------------------------- 1 | 1 2 2 | -------------------------------------------------------------------------------- /examples/testdata/A+B Problem/aplusb1.out: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /examples/testdata/Special Judge/a1.in: -------------------------------------------------------------------------------- 1 | 1 2 2 | -------------------------------------------------------------------------------- /examples/testdata/Special Judge/a1.out: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /examples/testdata/Special Judge/chk.cc: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) { 5 | /* 6 | * inf:输入 7 | * ouf:选手输出 8 | * ans:标准输出 9 | */ 10 | registerTestlibCmd(argc, argv); 11 | double pans = ouf.readDouble(), jans = ans.readDouble(); 12 | if (pans == jans) quitf(_ok, "Good job\n"); 13 | else quitf(_wa, "Too big or too small, expected %f, found %f\n", jans, pans); 14 | } 15 | -------------------------------------------------------------------------------- /examples/testdata/Special Judge/config.yaml: -------------------------------------------------------------------------------- 1 | type: default 2 | checker_type: testlib 3 | checker: chk.cc -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1739446958, 6 | "narHash": "sha256-+/bYK3DbPxMIvSL4zArkMX0LQvS7rzBKXnDXLfKyRVc=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "2ff53fe64443980e139eaa286017f53f88336dd0", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "nixos-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Hydro development environment"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 6 | }; 7 | 8 | outputs = { self, nixpkgs }: 9 | let 10 | system = "x86_64-linux"; 11 | pkgs = nixpkgs.legacyPackages.${system}; 12 | in { 13 | packages.${system}.default = pkgs.hello; 14 | devShells.${system}.default = pkgs.mkShell { 15 | buildInputs = [ 16 | pkgs.nodejs 17 | pkgs.yarn-berry 18 | pkgs.pm2 19 | pkgs.git 20 | pkgs.gcc 21 | pkgs.go-judge 22 | pkgs.mongodb-ce 23 | ]; 24 | }; 25 | }; 26 | } -------------------------------------------------------------------------------- /framework/framework/index.ts: -------------------------------------------------------------------------------- 1 | export * from './server'; 2 | export { default as serializer } from './serializer'; 3 | export * from './decorators'; 4 | export * from './validator'; 5 | export * from './error'; 6 | export * from './router'; 7 | export * from './interface'; 8 | export * from './api'; 9 | -------------------------------------------------------------------------------- /framework/framework/serializer.ts: -------------------------------------------------------------------------------- 1 | import type { HandlerCommon } from './server'; 2 | 3 | export default function serializer(ignoreSerializeFunction = false, h?: HandlerCommon) { 4 | return (k: string, v: any) => { 5 | if (k.startsWith('_') && k !== '_id') return undefined; 6 | if (typeof v === 'bigint') return `BigInt::${v.toString()}`; 7 | if (!ignoreSerializeFunction && v && typeof v === 'object' 8 | && 'serialize' in v && typeof v.serialize === 'function') return v.serialize(h); 9 | return v; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /framework/framework/tests/validator.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { describe, it } from 'node:test'; 3 | import { Types } from '../validator'; 4 | 5 | const k = Symbol.for('schemastery'); 6 | 7 | describe('validator', () => { 8 | it('NumericArray', () => { 9 | expect(Types.NumericArray('1,2,3')).to.deep.equal([1, 2, 3]); 10 | expect(Types.NumericArray([1, 2, 3])).to.deep.equal([1, 2, 3]); 11 | expect(() => Types.NumericArray('123a')).to.throw(); 12 | }); 13 | 14 | it('CommaSeperatedArray', () => { 15 | expect(Types.CommaSeperatedArray('1,2,3')).to.deep.equal(['1', '2', '3']); 16 | expect(Types.CommaSeperatedArray([1, 2, 3])).to.deep.equal(['1', '2', '3']); 17 | expect(Types.CommaSeperatedArray('123a')).to.deep.equal(['123a']); 18 | expect(k in Types.CommaSeperatedArray).to.eq(true); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /framework/register/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/register", 3 | "version": "1.0.3", 4 | "license": "MIT", 5 | "main": "index.js", 6 | "engines": { 7 | "node": ">=16" 8 | }, 9 | "dependencies": { 10 | "esbuild": "0.25.2", 11 | "source-map-support": "^0.5.21" 12 | }, 13 | "devDependencies": { 14 | "@types/source-map-support": "^0.5.10" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /install/docker/backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20 2 | ADD ./entrypoint.sh /root/entrypoint.sh 3 | RUN yarn global add pm2 hydrooj @hydrooj/ui-default 4 | RUN chmod +x /root/entrypoint.sh && \ 5 | mkdir -p /root/.hydro 6 | ENTRYPOINT /root/entrypoint.sh 7 | -------------------------------------------------------------------------------- /install/docker/backend/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ROOT=/root/.hydro 4 | 5 | if [ ! -f "$ROOT/addon.json" ]; then 6 | echo '["@hydrooj/ui-default"]' > "$ROOT/addon.json" 7 | fi 8 | 9 | if [ ! -f "$ROOT/config.json" ]; then 10 | echo '{"host": "oj-mongo", "port": "27017", "name": "hydro", "username": "", "password": ""}' > "$ROOT/config.json" 11 | fi 12 | 13 | if [ ! -f "$ROOT/first" ]; then 14 | echo "for marking use only!" > "$ROOT/first" 15 | 16 | hydrooj cli user create systemjudge@systemjudge.local root rootroot 17 | hydrooj cli user setSuperAdmin 2 18 | fi 19 | 20 | pm2-runtime start hydrooj 21 | -------------------------------------------------------------------------------- /install/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | 5 | # Warning: mongodb here is not password-protected. 6 | # DO NOT EXPOSE THIS SERVICE TO THE PUBLIC. 7 | oj-mongo: 8 | image: mongo 9 | container_name: oj-mongo 10 | restart: always 11 | volumes: 12 | - ./data/mongo:/data/db 13 | 14 | oj-backend: 15 | build: ./backend 16 | container_name: oj-backend 17 | restart: always 18 | depends_on: 19 | - oj-mongo 20 | volumes: 21 | - ./data/file:/data/file 22 | - ./data/backend:/root/.hydro 23 | ports: 24 | - "0.0.0.0:80:8888" # In docker mode, change THIS port instead of port in system settings! 25 | 26 | oj-judge: 27 | build: ./judge 28 | container_name: oj-judge 29 | restart: always 30 | privileged: true 31 | depends_on: 32 | - oj-backend 33 | volumes: 34 | - ./data/judge:/root/.config/hydro 35 | -------------------------------------------------------------------------------- /install/docker/judge/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20 2 | 3 | RUN apt-get -qq update && \ 4 | apt-get install -y \ 5 | gcc \ 6 | python3 \ 7 | g++ \ 8 | fp-compiler \ 9 | openjdk-8-jdk-headless \ 10 | python \ 11 | php7.0-cli \ 12 | rustc \ 13 | haskell-platform \ 14 | libjavascriptcoregtk-4.0-bin \ 15 | golang \ 16 | ruby \ 17 | mono-runtime \ 18 | mono-mcs 19 | 20 | ADD ./entrypoint.sh /root/entrypoint.sh 21 | ADD ./judge.yaml /root/judge.yaml 22 | RUN chmod +x /root/entrypoint.sh 23 | 24 | RUN yarn global add pm2 @hydrooj/hydrojudge && \ 25 | wget https://github.com/criyle/go-judge/releases/download/v1.8.0/go-judge_1.8.0_linux_amd64 -O /usr/bin/sandbox && \ 26 | chmod +x /usr/bin/sandbox 27 | 28 | ENTRYPOINT /root/entrypoint.sh 29 | -------------------------------------------------------------------------------- /install/docker/judge/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | pm2 start sandbox 4 | pm2-runtime start hydrojudge 5 | -------------------------------------------------------------------------------- /install/docker/judge/judge.yaml: -------------------------------------------------------------------------------- 1 | hosts: 2 | localhost: 3 | type: hydro 4 | server_url: http://oj-backend:8888/ 5 | uname: root 6 | password: rootroot 7 | detail: true 8 | -------------------------------------------------------------------------------- /install/helm-single/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /install/helm-single/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: hydro-helm 3 | description: A Helm chart for Hydro Deployment 4 | type: application 5 | version: 0.1.0 6 | appVersion: "1.16.0" 7 | -------------------------------------------------------------------------------- /install/helm-single/readme.md: -------------------------------------------------------------------------------- 1 | ## 该部署方式非官方维护,仅适用于有经验的K8s集群运维人员修改使用 2 | ## 普通用户请务必使用自动脚本安装,这可大大提高您一次成功的可能性 3 | 4 | ## It is for testing purposes only and is not production-ready. 5 | ## TL;DR 6 | 7 | 首次部署完毕后,不会自动创建用户,请手动在Backend的Pod中执行 8 | 9 | ``` 10 | hydrooj cli user create systemjudge@systemjudge.local root rootroot 11 | hydrooj cli user setSuperAdmin 2 12 | ``` 13 | 14 | Helm Chart示例中尚未完全适配多节点以及HA需求。主要体现在 15 | - Mongo的单节点部署 16 | - 为了理解和调试便利,后端容器`/data/file`和`/root/.hydro`,Mongo容器`/data/db`,评测机容器`/root/.config/hydro`使用了HostPath。 17 | 18 | 19 | 由于Judge需要以特权容器运行(cgroup所需),建议将Backend和Judge调度到不同的节点上。 20 | 21 | 本部署方式暂不支持本地构建镜像,请根据组织架构场景下的基础设施,自行处理镜像仓库问题。 22 | 23 | -------------------------------------------------------------------------------- /install/helm-single/templates/backend.config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: backend-config-map 5 | labels: 6 | app: backend 7 | namespace: hydro-namespace 8 | data: 9 | addon.json: |- 10 | {{.Values.Backend.AddonJson | nindent 4}} 11 | 12 | config.json: |- 13 | {{.Values.Backend.ConfigJson | nindent 4}} 14 | -------------------------------------------------------------------------------- /install/helm-single/templates/backend.service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: backend 6 | name: backend-service 7 | namespace: hydro-namespace 8 | spec: 9 | ports: 10 | - port: {{.Values.Backend.ClusterPort}} 11 | name: backend-cluster-endpoint 12 | protocol: TCP 13 | targetPort: 80 14 | selector: 15 | app: backend 16 | type: ClusterIP 17 | -------------------------------------------------------------------------------- /install/helm-single/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: hydro-ingress 5 | annotations: 6 | kubernetes.io/ingress.class: traefik 7 | ingress.kubernetes.io/ssl-redirect: "false" 8 | namespace: hydro-namespace 9 | spec: 10 | rules: 11 | - host: "backend.boo.foo" # 如果不需要当然也可以去掉HostName 12 | http: 13 | paths: 14 | - path: / 15 | pathType: Prefix 16 | backend: 17 | service: 18 | name: backend-service-service 19 | port: 20 | name: backend-cluster-endpoint 21 | -------------------------------------------------------------------------------- /install/helm-single/templates/judge.config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: judge-config-map 5 | labels: 6 | app: judge 7 | namespace: hydro-namespace 8 | data: 9 | judge.yaml: |- 10 | {{.Values.Judge.JudgeYaml | nindent 4}} 11 | -------------------------------------------------------------------------------- /install/helm-single/templates/mongo.service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: mongo 6 | name: mongo-service 7 | namespace: hydro-namespace 8 | spec: 9 | ports: 10 | - port: {{.Values.Mongo.ClusterPort}} 11 | name: mongo-cluster-endpoint 12 | protocol: TCP 13 | targetPort: 27017 14 | selector: 15 | app: mongo 16 | type: ClusterIP 17 | --- 18 | apiVersion: v1 19 | kind: Service 20 | metadata: 21 | labels: 22 | app: mongo 23 | name: mongo-loadbalance-service 24 | namespace: hydro-namespace 25 | spec: 26 | ports: 27 | - port: {{.Values.Mongo.LoadBalancerPort}} 28 | name: mongo-endpoint 29 | protocol: TCP 30 | targetPort: 27017 31 | selector: 32 | app: mongo 33 | type: LoadBalancer 34 | -------------------------------------------------------------------------------- /install/helm-single/templates/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: hydro-namespace 5 | labels: 6 | name: hydro-namespace-label 7 | -------------------------------------------------------------------------------- /install/nix/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { system = "x86_64-linux"; } }: 2 | 3 | let 4 | hydro = import (pkgs.fetchFromGitHub { 5 | owner = "hydro-dev"; 6 | repo = "nix-channel"; 7 | rev = "master"; 8 | sha256 = "sha256-EqPU9n4H3EteJqFFv6Seeo9DZxFc3Mdu8Y1y/fjZJ80="; 9 | }) {}; 10 | in pkgs.dockerTools.buildImage { 11 | name = "hydrooj/web-base"; 12 | tag = "latest"; 13 | 14 | copyToRoot = pkgs.buildEnv { 15 | name = "hydro-web"; 16 | paths = [ 17 | hydro.mongodb4 18 | pkgs.nodejs 19 | pkgs.yarn 20 | ]; 21 | ignoreCollisions = true; 22 | pathsToLink = [ "/bin" ]; 23 | }; 24 | 25 | config = { 26 | WorkingDir = "/data"; 27 | Volumes = { "/data" = { }; }; 28 | ExposedPorts = { 29 | "8888" = { }; 30 | }; 31 | Cmd = [ "hydrooj" ]; 32 | }; 33 | } -------------------------------------------------------------------------------- /install/nix/env.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { system = "x86_64-linux"; } }: 2 | 3 | let 4 | mongo = pkgs.callPackage ./mongo.nix {}; 5 | in pkgs.buildEnv { 6 | name = "hydro-env"; 7 | paths = [ 8 | mongo 9 | pkgs.nodejs 10 | pkgs.yarn 11 | pkgs.git 12 | ]; 13 | } -------------------------------------------------------------------------------- /install/nix/hydro.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import {} }: 2 | 3 | pkgs.stdenv.mkDerivation { 4 | name = "hydro-0.0.0"; 5 | system = "x86_64-linux"; 6 | #TODO install from cache 7 | src = ../../.yarn/cache; 8 | unpackPhase = "ls $src"; 9 | 10 | meta = { 11 | description = "Hydro"; 12 | homepage = https://hydro.js.org/; 13 | maintainers = [ "undefined " ]; 14 | platforms = [ "x86_64-linux" ]; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /install/reset.sh: -------------------------------------------------------------------------------- 1 | pm2 stop mongodb 2 | rm -rf /data/db /data/file 3 | mkdir /data/db /data/file 4 | pm2 start mongod 5 | sleep 3 6 | db_password=$(cat /dev/urandom | head -n 10 | md5sum | head -c 20) 7 | echo "db.createUser({ 8 | user: 'hydro', 9 | pwd: '$db_password', 10 | roles: [ 11 | { role: 'readWrite', db: 'hydro' } 12 | ] 13 | })" >/tmp/createUser.js 14 | mongosh 127.0.0.1:27017/hydro /tmp/createUser.js 15 | echo "{\"host\":\"127.0.0.1\",\"port\":\"27017\",\"name\":\"hydro\",\"username\":\"hydro\",\"password\":\"$db_password\"}" >~/.hydro/config.json 16 | pm2 stop mongod 17 | pm2 del mongod 18 | pm2 start mongodb 19 | pm2 restart all 20 | pm2 save 21 | -------------------------------------------------------------------------------- /packages/a11y/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/a11y", 3 | "version": "0.0.3" 4 | } 5 | -------------------------------------------------------------------------------- /packages/blog/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/blog", 3 | "version": "0.1.0-beta.0", 4 | "license": "AGPL-3.0-or-later" 5 | } 6 | -------------------------------------------------------------------------------- /packages/center/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/center", 3 | "version": "0.2.10", 4 | "main": "index.ts", 5 | "repository": "https://github.com/hydro-dev/Hydro.git", 6 | "author": "undefined ", 7 | "license": "AGPL-3.0-or-later", 8 | "preferUnplugged": true, 9 | "peerDependencies": { 10 | "hydrooj": "*" 11 | }, 12 | "dependencies": { 13 | "crypto-js": "^4.2.0", 14 | "semver": "^7.7.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lang'; 2 | export * from './status'; 3 | export * from './types'; 4 | export * from './subtask'; 5 | -------------------------------------------------------------------------------- /packages/common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/common", 3 | "main": "./index.ts", 4 | "version": "0.0.2", 5 | "dependencies": { 6 | "@hydrooj/utils": "workspace:^" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/components/frontend/Dom.tsx: -------------------------------------------------------------------------------- 1 | import { omit } from 'lodash'; 2 | import React from 'react'; 3 | 4 | export default function DomComponent(props: React.HTMLAttributes & { childDom: HTMLElement }) { 5 | const [dom, setDom] = React.useState(null); 6 | React.useEffect(() => { 7 | if (!dom) return; 8 | dom.appendChild(props.childDom); 9 | return () => { // eslint-disable-line consistent-return 10 | dom.removeChild(props.childDom); 11 | }; 12 | }, [dom]); 13 | return
; 14 | } 15 | -------------------------------------------------------------------------------- /packages/components/frontend/Icon.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import PropTypes from 'prop-types'; 3 | import React from 'react'; 4 | 5 | export default function IconComponent(props: { name: string, className?: string } & React.HTMLAttributes) { 6 | const { 7 | name, 8 | className, 9 | ...rest 10 | } = props; 11 | const cn = classNames(className, `icon icon-${name}`); 12 | return ( 13 | 14 | ); 15 | } 16 | 17 | IconComponent.propTypes = { 18 | name: PropTypes.string.isRequired, 19 | className: PropTypes.string, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/components/frontend/Layout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function Row({ children }: { children: React.ReactNode }) { 4 | return
5 | {children} 6 |
; 7 | } 8 | 9 | export function Column({ children, md }: { children: React.ReactNode, md?: number }) { 10 | return
11 | {children} 12 |
; 13 | } 14 | -------------------------------------------------------------------------------- /packages/components/frontend/Notification.tsx: -------------------------------------------------------------------------------- 1 | import { toast } from 'react-toastify'; 2 | 3 | export default class Notification { 4 | type: string; 5 | action: any; 6 | $dom: JQuery; 7 | $n: JQuery; 8 | duration: number; 9 | autoHideTimer?: NodeJS.Timeout; 10 | 11 | static async success(message: string, duration?: number) { 12 | return toast.success(message, { autoClose: duration }); 13 | } 14 | 15 | static async info(message: string, duration?: number) { 16 | return toast.info(message, { autoClose: duration }); 17 | } 18 | 19 | static async warn(message: string, duration?: number) { 20 | return toast.warning(message, { autoClose: duration }); 21 | } 22 | 23 | static async error(message: string, duration?: number) { 24 | return toast.error(message, { autoClose: duration }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/components/frontend/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ComponentsProvider } from './provider'; 2 | export { default as ConfigEditor } from './ConfigEditor'; 3 | export { default as Icon } from './Icon'; 4 | export { default as Notification } from './Notification'; 5 | export { default as AutoComplete, type AutoCompleteHandle, type AutoCompleteProps } from './autocomplete/AutoComplete'; 6 | export { default as CustomSelectAutoComplete } from './autocomplete/CustomSelectAutoComplete'; 7 | -------------------------------------------------------------------------------- /packages/components/frontend/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg?react' { 2 | import { ComponentType } from 'react'; 3 | const content: ComponentType; 4 | export default content; 5 | } 6 | declare module '*.vue' { 7 | const content: any; 8 | export default content; 9 | } 10 | declare module '*.css' { 11 | const content: string; 12 | export default content; 13 | } 14 | declare module '*.styl' { 15 | const content: string; 16 | export default content; 17 | } 18 | declare module '*.scss' { 19 | const content: string; 20 | export default content; 21 | } 22 | -------------------------------------------------------------------------------- /packages/components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/components", 3 | "version": "1.0.0-beta.3", 4 | "main": "./frontend/index.ts", 5 | "dependencies": { 6 | "allotment": "^1.20.3", 7 | "diff": "^8.0.2", 8 | "js-yaml": "^4.1.0", 9 | "lodash": "^4.17.21", 10 | "react-dnd": "^16.0.1", 11 | "react-dnd-html5-backend": "^16.0.1", 12 | "react-toastify": "^11.0.5", 13 | "schemastery-react": "^0.1.3" 14 | }, 15 | "devDependencies": { 16 | "@types/diff": "^8.0.0", 17 | "@types/js-yaml": "^4.0.9", 18 | "@types/lodash": "^4.17.17", 19 | "sass": "^1.89.1" 20 | }, 21 | "peerDependencies": { 22 | "react": "*", 23 | "react-dom": "*", 24 | "schemastery": "*" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/elastic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/elastic-search", 3 | "version": "1.2.0-beta.2", 4 | "main": "index.ts", 5 | "repository": "https://github.com/hydro-dev/Hydro", 6 | "author": "undefined ", 7 | "license": "AGPL-3.0-or-later", 8 | "dependencies": { 9 | "@elastic/elasticsearch": "^9.0.2" 10 | }, 11 | "engines": { 12 | "hydrooj": ">=5.0.0-alpha.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/fps-importer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/fps-importer", 3 | "version": "1.6.0-beta.1", 4 | "description": "Import FPS problems", 5 | "main": "index.ts", 6 | "repository": "https://github.com/hydro-dev/Hydro.git", 7 | "author": "undefined ", 8 | "license": "SEE LICENSE IN LICENSE", 9 | "preferUnplugged": true, 10 | "dependencies": { 11 | "decode-html": "^2.0.0", 12 | "xml2js": "^0.6.2" 13 | }, 14 | "devDependencies": { 15 | "@types/xml2js": "^0.4.14" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/geoip/README.md: -------------------------------------------------------------------------------- 1 | # GeoIp 2 | 3 | 显示用户登录地。 4 | 5 | IP geo-location data is provided by [Maxmind](http://www.maxmind.com) 6 | -------------------------------------------------------------------------------- /packages/geoip/download.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -f "./GeoLite2-City.mmdb" ]; then 4 | if [ -n "${LICENSE_KEY}" ]; then 5 | wget -O ./GeoLite2-City.tar.gz "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=${LICENSE_KEY}&suffix=tar.gz" 6 | tar zxvf ./GeoLite2-City.tar.gz -C . 7 | mv ./GeoLite2-City_*/GeoLite2-City.mmdb ./GeoLite2-City.mmdb 8 | rm -r ./GeoLite2-City_* ./GeoLite2-City.tar.gz 9 | fi 10 | fi 11 | -------------------------------------------------------------------------------- /packages/geoip/locale/zh.yaml: -------------------------------------------------------------------------------- 1 | geoip_locale: zh-CN -------------------------------------------------------------------------------- /packages/geoip/locale/zh_TW.yaml: -------------------------------------------------------------------------------- 1 | geoip_locale: zh-CN -------------------------------------------------------------------------------- /packages/geoip/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/geoip", 3 | "version": "1.4.0-beta.0", 4 | "main": "index.ts", 5 | "repository": "git@github.com:hydro-dev/Hydro.git", 6 | "author": "undefined ", 7 | "license": "AGPL-3.0-or-later", 8 | "dependencies": { 9 | "maxmind": "^4.3.27" 10 | }, 11 | "preferUnplugged": true 12 | } 13 | -------------------------------------------------------------------------------- /packages/hydrojudge/.npmignore: -------------------------------------------------------------------------------- 1 | vendor/testlib/tests 2 | node_modules -------------------------------------------------------------------------------- /packages/hydrojudge/bin/hydrojudge.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const argv = require('cac')().parse(); 4 | require('@hydrooj/register'); 5 | 6 | if (argv.args[0] === 'cache') require('../src/cache')(); 7 | else if (argv.args[0] === 'terminal') require('../src/terminal')(); 8 | else require('../src/daemon')(); 9 | -------------------------------------------------------------------------------- /packages/hydrojudge/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/hydrojudge", 3 | "bin": "bin/hydrojudge.js", 4 | "version": "3.5.0-beta.3", 5 | "main": "./src/index.ts", 6 | "author": "undefined ", 7 | "repository": "https://github.com/hydro-dev/Hydro.git", 8 | "dependencies": { 9 | "@hydrooj/common": "workspace:^", 10 | "@hydrooj/utils": "workspace:^", 11 | "@zip.js/zip.js": "^2.7.62", 12 | "cac": "^6.7.14", 13 | "p-queue": "^8.1.0", 14 | "schemastery": "^3.16.1", 15 | "semver": "^7.7.2", 16 | "shell-quote": "^1.8.3", 17 | "superagent": "^10.2.1", 18 | "ws": "^8.18.2" 19 | }, 20 | "preferUnplugged": true, 21 | "license": "AGPL-3.0-or-later", 22 | "devDependencies": { 23 | "@types/shell-quote": "^1.7.5", 24 | "@types/ws": "^8.18.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/hydrojudge/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Context } from 'hydrooj'; 2 | import { JudgeSettings, overrideConfig } from './config'; 3 | 4 | export const Config = JudgeSettings; 5 | 6 | export function apply(ctx: Context, config: ReturnType) { 7 | overrideConfig(config); 8 | if (process.env.NODE_APP_INSTANCE !== '0') return; 9 | // eslint-disable-next-line consistent-return 10 | if (!config.disable) return require('./hosts/builtin').apply(ctx); 11 | } 12 | -------------------------------------------------------------------------------- /packages/hydrojudge/src/judge/index.ts: -------------------------------------------------------------------------------- 1 | import * as communication from './communication'; 2 | import * as def from './default'; 3 | import * as generate from './generate'; 4 | import * as hack from './hack'; 5 | import * as interactive from './interactive'; 6 | import { Context } from './interface'; 7 | import * as objective from './objective'; 8 | import * as run from './run'; 9 | import * as submit_answer from './submit_answer'; 10 | 11 | export = { 12 | default: def, generate, interactive, communication, run, submit_answer, objective, hack, 13 | } as Record }>; 14 | -------------------------------------------------------------------------------- /packages/hydrojudge/src/judge/interface.ts: -------------------------------------------------------------------------------- 1 | import PQueue from 'p-queue'; 2 | import { NormalizedSubtask } from '@hydrooj/common'; 3 | import { Execute } from '../interface'; 4 | import { JudgeTask } from '../task'; 5 | 6 | export type Context = JudgeTask & RuntimeContext; 7 | 8 | export interface RuntimeContext { 9 | total_score?: number; 10 | total_status?: number; 11 | total_time?: number; 12 | total_memory?: number; 13 | 14 | queue?: PQueue; 15 | errored?: boolean; 16 | rerun?: number; 17 | analysis?: boolean; 18 | failed?: Record; 19 | 20 | execute?: Execute; 21 | checker?: Execute; 22 | executeInteractor?: Execute; 23 | executeManager?: Execute; 24 | executeUser?: Execute; 25 | } 26 | 27 | export interface ContextSubTask { 28 | subtask: NormalizedSubtask; 29 | score: number; 30 | status: number; 31 | } 32 | -------------------------------------------------------------------------------- /packages/hydrojudge/src/log.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@hydrooj/utils/lib/utils'; 2 | export * from '@hydrooj/utils/lib/utils'; 3 | export default new Logger('judge'); 4 | -------------------------------------------------------------------------------- /packages/hydrooj/README.md: -------------------------------------------------------------------------------- 1 | # Hydro Online Judge Core Package 2 | 3 | Copyright [@hydro-dev](https://github.com/hydro-dev) team. 4 | 5 | Other information please refer to [Github repository](https://github.com/hydro-dev/Hydro) and [Official document](https://hydro.js.org) . 6 | -------------------------------------------------------------------------------- /packages/hydrooj/src/handler/compat.ts: -------------------------------------------------------------------------------- 1 | import { Handler } from '../service/server'; 2 | 3 | class ProblemCategoryCompatHandler extends Handler { 4 | async get({ category }) { 5 | this.response.redirect = this.url('problem_main', { query: { q: `category:${category}` } }); 6 | } 7 | } 8 | 9 | export const apply = (ctx) => { 10 | ctx.Route('problem_category_compat', '/p/category/:category', ProblemCategoryCompatHandler); 11 | }; 12 | -------------------------------------------------------------------------------- /packages/hydrooj/src/lib/hash.hydro.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto'; 2 | 3 | function hash(password: string, salt: string): Promise { 4 | return new Promise((resolve, reject) => { 5 | crypto.pbkdf2(password, salt, 100000, 64, 'sha256', (err, key) => { 6 | if (err) reject(err); 7 | else resolve(key.toString('hex').substring(0, 64)); 8 | }); 9 | }); 10 | } 11 | 12 | export default hash; 13 | global.Hydro.module.hash.hydro = hash; 14 | -------------------------------------------------------------------------------- /packages/hydrooj/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | import './i18n'; 2 | import './mail'; 3 | import './hash.hydro'; 4 | import './ui'; 5 | import './testdataConfig'; 6 | import './difficulty'; 7 | import './content'; 8 | import './avatar'; 9 | -------------------------------------------------------------------------------- /packages/hydrooj/src/lib/mime.ts: -------------------------------------------------------------------------------- 1 | import { lookup } from 'mime-types'; 2 | 3 | export default function mime(file: string) { 4 | const identifer = file.toLowerCase(); 5 | return (['.in', '.out', '.ans'].some((i) => identifer.endsWith(i))) 6 | ? 'text/plain' 7 | : lookup(file) || 'application/octet-stream'; 8 | } 9 | -------------------------------------------------------------------------------- /packages/hydrooj/src/lib/verifyTFA.ts: -------------------------------------------------------------------------------- 1 | import notp from 'notp'; 2 | import b32 from 'thirty-two'; 3 | 4 | export function verifyTFA(secret: string, code?: string) { 5 | if (!code || !code.length) return null; 6 | const bin = b32.decode(secret); 7 | return notp.totp.verify(code.replace(/\W+/g, ''), bin); 8 | } 9 | -------------------------------------------------------------------------------- /packages/hydrooj/src/logger.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@hydrooj/utils/lib/utils'; 2 | export { Logger } from '@hydrooj/utils/lib/utils'; 3 | 4 | global.Hydro.Logger = Logger; 5 | export const logger = new Logger('*'); 6 | global.Hydro.logger = logger; 7 | -------------------------------------------------------------------------------- /packages/hydrooj/src/service/layers/user.ts: -------------------------------------------------------------------------------- 1 | import type { KoaContext } from '@hydrooj/framework'; 2 | import { PERM } from '../../model/builtin'; 3 | import UserModel from '../../model/user'; 4 | 5 | export default async (ctx: KoaContext, next) => { 6 | // User Layer 7 | const { args, domain } = ctx.HydroContext; 8 | const domainId = domain ? args.domainId : 'system'; 9 | let user = await UserModel.getById(domainId, ctx.session.uid, ctx.session.scope); 10 | if (!user) { 11 | ctx.session.uid = 0; 12 | ctx.session.scope = PERM.PERM_ALL.toString(); 13 | user = await UserModel.getById(domainId, ctx.session.uid, ctx.session.scope); 14 | } 15 | if (user._id === 0) delete user.viewLang; 16 | ctx.HydroContext.user = await user.private(); 17 | await next(); 18 | }; 19 | -------------------------------------------------------------------------------- /packages/import-hoj/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/import-hoj", 3 | "version": "0.0.6-beta.1", 4 | "description": "Import HOJ problem export", 5 | "main": "index.ts", 6 | "repository": "https://github.com/hydro-dev/Hydro.git", 7 | "author": "panda ", 8 | "license": "AGPL-3.0-or-later", 9 | "preferUnplugged": true 10 | } 11 | -------------------------------------------------------------------------------- /packages/import-qduoj/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/import-qduoj", 3 | "version": "1.6.0-beta.2", 4 | "description": "Import QDUOJ problem export", 5 | "main": "index.ts", 6 | "repository": "https://github.com/hydro-dev/Hydro.git", 7 | "author": "undefined ", 8 | "license": "AGPL-3.0-or-later", 9 | "preferUnplugged": true 10 | } 11 | -------------------------------------------------------------------------------- /packages/login-with-github/README.md: -------------------------------------------------------------------------------- 1 | # Login-With-Github 2 | -------------------------------------------------------------------------------- /packages/login-with-github/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/login-with-github", 3 | "version": "0.3.0-beta.1", 4 | "main": "index.ts", 5 | "repository": "git@github.com:hydro-dev/Hydro.git", 6 | "author": "undefined ", 7 | "license": "AGPL-3.0-or-later", 8 | "preferUnplugged": true 9 | } 10 | -------------------------------------------------------------------------------- /packages/login-with-google/README.md: -------------------------------------------------------------------------------- 1 | # Login-With-Google 2 | -------------------------------------------------------------------------------- /packages/login-with-google/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/login-with-google", 3 | "version": "0.3.0-beta.0", 4 | "main": "index.ts", 5 | "repository": "git@github.com:hydro-dev/Hydro.git", 6 | "author": "undefined ", 7 | "license": "AGPL-3.0-or-later", 8 | "preferUnplugged": true 9 | } 10 | -------------------------------------------------------------------------------- /packages/migrate/README.md: -------------------------------------------------------------------------------- 1 | # migrate 2 | 3 | 4 | 这个模块用于从其他系统批量导入数据。 5 | 迁移过程中请保持网络畅通。 6 | 7 | ## migrate-vijos 8 | 9 | 从Vijos4.0的数据库导入数据。 10 | 迁移前,您需要指定数据来源。 11 | **请不要**将 Hydro 正在使用的数据库设置为数据来源的数据库。 12 | 迁移过程中,Hydro 的以下内容会被**清空**: 13 | 题目列表,提交记录,用户列表,比赛列表,比赛成绩表,训练列表,训练进度,站内消息,题解,讨论 14 | 以下内容将被迁移: 15 | 题目与测试数据、题解,讨论与回复,比赛、训练、作业相关数据,提交记录,用户列表,站内消息。 16 | 17 | ## migrate-hustoj 18 | 19 | 从HustOJ的数据库导入数据。 20 | 迁移过程中,Hydro 的内容会被**清空**,以下内容将被迁移: 21 | 题目与测试数据,用户列表。 22 | -------------------------------------------------------------------------------- /packages/migrate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/migrate", 3 | "version": "0.5.0-beta.2", 4 | "main": "index.ts", 5 | "repository": "git@github.com:hydro-dev/Hydro.git", 6 | "author": "undefined ", 7 | "license": "AGPL-3.0-or-later", 8 | "dependencies": { 9 | "mariadb": "^3.4.2", 10 | "mongodb": "^6.17.0", 11 | "turndown": "^7.2.0", 12 | "xml2js": "^0.6.2" 13 | }, 14 | "devDependencies": { 15 | "@types/turndown": "^5.0.5", 16 | "@types/xml2js": "^0.4.14" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/onlyoffice/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/onlyoffice", 3 | "version": "1.5.0-beta.0", 4 | "main": "index.ts", 5 | "repository": "git@github.com:hydro-dev/Hydro.git", 6 | "author": "undefined ", 7 | "preferUnplugged": true, 8 | "license": "AGPL-3.0-or-later", 9 | "dependencies": { 10 | "jsonwebtoken": "^9.0.2" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/onsite-toolkit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/onsite-toolkit", 3 | "version": "0.1.0-beta.2", 4 | "dependencies": { 5 | "@react-spring/web": "^10.0.1", 6 | "react-use": "^17.6.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/onsite-toolkit/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/onsite-toolkit/public/logo.png -------------------------------------------------------------------------------- /packages/onsite-toolkit/templates/resolver.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/html5.html" %} 2 | {% block body %} 3 | {{ set(UiContext, 'payload', payload) }} 4 |
5 | #Rank 6 | 7 | Contest 8 | @Hydro/TinyResolver 9 | 10 | Solved 11 | Penalty 12 |
13 |
14 | 15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /packages/prom-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/prom-client", 3 | "version": "0.2.0-beta.1", 4 | "main": "index.ts", 5 | "dependencies": { 6 | "prom-client": "^15.1.3" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/scoreboard-xcpcio/.npmignore: -------------------------------------------------------------------------------- 1 | public/ -------------------------------------------------------------------------------- /packages/scoreboard-xcpcio/install.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | const dist = path.join(path.dirname(require.resolve('@xcpcio/board-app/package.json')), 'dist'); 5 | const target = path.join(__dirname, 'public'); 6 | if (fs.existsSync(target)) { 7 | fs.rmSync(target, { recursive: true }); 8 | } 9 | fs.mkdirSync(target, { recursive: true }); 10 | fs.cpSync(path.join(dist, 'assets'), path.join(target, 'assets'), { recursive: true }); 11 | fs.cpSync(path.join(dist, 'index.html'), path.join(target, 'assets/board.html')); 12 | console.log('Copied board app to', target); 13 | -------------------------------------------------------------------------------- /packages/scoreboard-xcpcio/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/scoreboard-xcpcio", 3 | "license": "AGPL-3.0-or-later", 4 | "version": "0.0.5-beta.1", 5 | "main": "index.ts", 6 | "scripts": { 7 | "postinstall": "node -r @hydrooj/register install.ts" 8 | }, 9 | "dependencies": { 10 | "@xcpcio/board-app": "^0.55.2" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/sonic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/sonic", 3 | "version": "1.4.0-beta.0", 4 | "description": "Sonic search service", 5 | "main": "index.ts", 6 | "preferUnplugged": true, 7 | "repository": "https://github.com/hydro-dev/Hydro.git", 8 | "author": "undefined ", 9 | "license": "AGPL-3.0-or-later", 10 | "dependencies": { 11 | "sonic-channel": "^1.3.1" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/telegram/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/telegram", 3 | "version": "0.0.2-beta.1" 4 | } 5 | -------------------------------------------------------------------------------- /packages/ui-default/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .cache/ 3 | /build/ 4 | /common/ 5 | /components/ 6 | /constant/ 7 | /misc/ 8 | /pages/ 9 | /static/ 10 | /utils/ 11 | __* 12 | -------------------------------------------------------------------------------- /packages/ui-default/README.md: -------------------------------------------------------------------------------- 1 | # Hydro-UI-Default 2 | 3 | Based on Vijos-UI-Framework 4 | 5 | See [vijos/vj4/vj4/ui/README.md](https://github.com/vijos/vj4/blob/master/vj4/ui/README.md) 6 | -------------------------------------------------------------------------------- /packages/ui-default/breakpoints.json: -------------------------------------------------------------------------------- 1 | { 2 | "mobile": 600, 3 | "desktop": 1000, 4 | "hd": 1280 5 | } 6 | -------------------------------------------------------------------------------- /packages/ui-default/build/index.js: -------------------------------------------------------------------------------- 1 | require('@hydrooj/register'); 2 | const main = require('./main'); 3 | 4 | if (!module.parent) main(); 5 | -------------------------------------------------------------------------------- /packages/ui-default/build/utils/root.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | export default function root(fn = '.') { 4 | return path.resolve(__dirname, '../../', fn); 5 | } 6 | -------------------------------------------------------------------------------- /packages/ui-default/common/color.inc.styl: -------------------------------------------------------------------------------- 1 | aqua = #7FDBFF 2 | blue = #0074D9 3 | navy = #001F3F 4 | teal = #39CCCC 5 | green = #2ECC40 6 | olive = #3D9970 7 | lime = #01FF70 8 | yellow = #FFDC00 9 | orange = #FF851B 10 | red = #FF4136 11 | fuchsia = #F012BE 12 | purple = #B10DC9 13 | maroon = #85144B 14 | white = #ffffff 15 | silver = #dddddd 16 | gray = #aaaaaa 17 | black = #111111 18 | -------------------------------------------------------------------------------- /packages/ui-default/common/common.inc.styl: -------------------------------------------------------------------------------- 1 | // This file is imported in all stylus source code 2 | breakpoints = json('../breakpoints.json', { hash: true }) 3 | rupture.mobile-cutoff = unit(breakpoints.mobile, 'px') 4 | rupture.desktop-cutoff = unit(breakpoints.desktop, 'px') 5 | rupture.hd-cutoff = unit(breakpoints.hd, 'px') 6 | 7 | @import '~vj/misc/.iconfont/webicon.inc.styl' 8 | @import 'color.inc.styl' 9 | @import 'variables.inc.styl' 10 | @import 'easing.inc.styl' 11 | @import 'rem.inc.styl' 12 | @import 'functions.inc.styl' 13 | -------------------------------------------------------------------------------- /packages/ui-default/common/functions.inc.styl: -------------------------------------------------------------------------------- 1 | word-wrap() 2 | overflow-wrap: break-word 3 | word-wrap: break-word 4 | -ms-word-break: break-all 5 | word-break: break-word 6 | -ms-hyphens: auto 7 | -moz-hyphens: auto 8 | -webkit-hyphens: auto 9 | hyphens: auto 10 | -------------------------------------------------------------------------------- /packages/ui-default/common/rem.inc.styl: -------------------------------------------------------------------------------- 1 | rem() { 2 | ret = (); 3 | for arg in arguments { 4 | push(ret, unit(arg / $font-size, 'rem')); 5 | } 6 | return ret; 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui-default/components/autocomplete/domainselectautocomplete.page.styl: -------------------------------------------------------------------------------- 1 | .menu.domain-select 2 | width: rem(200px) 3 | 4 | .domain-select__id 5 | font-size: rem($font-size-small) 6 | color: $userselect-uid-color 7 | -------------------------------------------------------------------------------- /packages/ui-default/components/autocomplete/problemselectautocomplete.page.styl: -------------------------------------------------------------------------------- 1 | .menu.problem-select 2 | width: rem(200px) 3 | 4 | .problem-select__id 5 | font-size: rem($font-size-small) 6 | color: $userselect-uid-color 7 | -------------------------------------------------------------------------------- /packages/ui-default/components/autocomplete/userselectautocomplete.page.styl: -------------------------------------------------------------------------------- 1 | .menu.user-select 2 | width: rem(200px) 3 | 4 | .user-select__uid 5 | font-size: rem($font-size-small) 6 | color: $userselect-uid-color 7 | -------------------------------------------------------------------------------- /packages/ui-default/components/contest/contest.page.ts: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import Notification from 'vj/components/notification'; 3 | import { AutoloadPage } from 'vj/misc/Page'; 4 | import { delay, i18n, request } from 'vj/utils'; 5 | 6 | const contestPage = new AutoloadPage('contestPage', () => { 7 | $('[data-contest-code]').on('click', (ev) => { 8 | ev.preventDefault(); 9 | // eslint-disable-next-line no-alert 10 | const code = prompt(i18n('Invitation code:')); 11 | request.post('', { 12 | operation: 'attend', 13 | code, 14 | }).then(() => { 15 | Notification.success(i18n('Successfully attended')); 16 | delay(1000).then(() => window.location.reload()); 17 | }).catch((e) => { 18 | Notification.error(e.message || e); 19 | }); 20 | }); 21 | }); 22 | 23 | export default contestPage; 24 | -------------------------------------------------------------------------------- /packages/ui-default/components/contest/contest_sidebar.page.styl: -------------------------------------------------------------------------------- 1 | .contest-sidebar__bg 2 | display: block 3 | background: #67AABB url(problem-contest-bg.png) right top no-repeat 4 | color: #FFF !important 5 | font-size: rem($font-size) 6 | padding: rem(20px 0) 7 | 8 | +retina() 9 | background-image: url(problem-contest-bg@2x.png) 10 | background-size: 131px 150px 11 | 12 | h1 13 | font-size: rem($font-size-title) 14 | color: #FFF 15 | text-shadow: 0 1px 3px rgba(#000, 0.5) 16 | 17 | &:hover 18 | text-decoration: none 19 | 20 | .contest-sidebar__status 21 | margin-top: rem(20px) 22 | -------------------------------------------------------------------------------- /packages/ui-default/components/contest/problem-contest-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/contest/problem-contest-bg.png -------------------------------------------------------------------------------- /packages/ui-default/components/contest/problem-contest-bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/contest/problem-contest-bg@2x.png -------------------------------------------------------------------------------- /packages/ui-default/components/discussion/discussion.page.styl: -------------------------------------------------------------------------------- 1 | .discussion-node-tag 2 | font-size: rem($font-size-small) 3 | display: inline-block 4 | line-height: rem(22px) 5 | padding: rem(0 10px) 6 | vertical-align: middle 7 | border-radius: rem(12px) 8 | border: 1px solid $supplementary-border-color 9 | 10 | &:hover 11 | background: $primary-color 12 | color: #FFF !important 13 | text-decoration: none 14 | border-color: $primary-color 15 | 16 | .icon 17 | margin-right: rem(5px) 18 | -------------------------------------------------------------------------------- /packages/ui-default/components/discussion/reaction.page.styl: -------------------------------------------------------------------------------- 1 | .reactions 2 | display: inline 3 | 4 | .reaction 5 | white-space: nowrap 6 | width: auto 7 | border-radius: 1.75rem 8 | overflow: visible 9 | font-size: rem($font-size-small) 10 | display: inline-block 11 | line-height: rem(22px) 12 | padding: rem(0 10px) 13 | vertical-align: middle 14 | border-radius: rem(12px) 15 | border: 1px solid $supplementary-border-color 16 | cursor: pointer 17 | &.active, &:hover 18 | text-decoration: none 19 | border-color: $primary-color 20 | color: #FFF !important 21 | &.active 22 | background-color: lighten($primary-color, 10%) !important 23 | &:hover 24 | background-color: lighten($primary-color, 30%) !important 25 | .emoji 26 | margin-right: rem(5px) 27 | 28 | .popover-reaction-item 29 | &:hover 30 | background-color: $toolbar-bg-hover -------------------------------------------------------------------------------- /packages/ui-default/components/dropdown/dropdown.page.styl: -------------------------------------------------------------------------------- 1 | .dropdown-target 2 | display: none 3 | 4 | .drop .dropdown-target 5 | display: block 6 | 7 | .drop.nav__dropdown 8 | max-height: calc(100% - 60px) 9 | overflow-x: hidden 10 | overflow-y: auto 11 | box-shadow: 0rem 0.125rem 0.4375rem rgba(0 0 0 , 30%); 12 | 13 | .drop.nav__dropdown.drop-open 14 | overflow-x: auto 15 | 16 | .dropdown 17 | .menu 18 | min-width: rem(150px) 19 | box-shadow: $menu-drop-shadow 20 | background: $menu-drop-bg-color 21 | font-size: rem($font-size-small) 22 | 23 | .menu__link 24 | padding: rem(8px 10px) 25 | 26 | .dropdown 27 | .drop-content 28 | transition: transform .1s ease-in-out 29 | transform: scale(0.9) translateZ(0) 30 | 31 | &.drop-after-open 32 | .drop-content 33 | transform: scale(1) translateZ(0) 34 | -------------------------------------------------------------------------------- /packages/ui-default/components/dropdown/dropdown.page.ts: -------------------------------------------------------------------------------- 1 | import { AutoloadPage } from 'vj/misc/Page'; 2 | import Dropdown from './Dropdown'; 3 | 4 | const dropdownPage = new AutoloadPage('dropdownPage', () => { 5 | Dropdown.registerLifeCycleHooks(); 6 | }); 7 | 8 | export default dropdownPage; 9 | -------------------------------------------------------------------------------- /packages/ui-default/components/editor/cmeditor.page.ts: -------------------------------------------------------------------------------- 1 | import { AutoloadPage } from 'vj/misc/Page'; 2 | import { delay } from 'vj/utils'; 3 | import CmEditor from '.'; 4 | 5 | function runSubstitute($container: JQuery) { 6 | for (const language of ['markdown', 'yaml', 'json', 'plain']) { 7 | $container.find(`textarea[data-${language}]`).get().forEach((element) => { 8 | const config: any = { language }; 9 | if ($(element).data('model')) config.model = $(element).data('model'); 10 | CmEditor.getOrConstruct($(element), config); 11 | }); 12 | } 13 | } 14 | 15 | const cmEditorPage = new AutoloadPage('cmEditorPage', () => { 16 | runSubstitute($('body')); 17 | $(document).on('vjContentNew', async (e) => { 18 | await delay(0); 19 | runSubstitute($(e.target)); 20 | }); 21 | }); 22 | 23 | export default cmEditorPage; 24 | -------------------------------------------------------------------------------- /packages/ui-default/components/editor/cmeditor.styl: -------------------------------------------------------------------------------- 1 | .decoration-hide 2 | color: grey !important 3 | background-color: grey !important 4 | 5 | .md-editor-preview code 6 | background-color: unset !important 7 | color: unset !important 8 | 9 | .md-editor-scrn pre.syntax-hl code 10 | padding-left: unset !important 11 | -------------------------------------------------------------------------------- /packages/ui-default/components/editor/textareaHandler.ts: -------------------------------------------------------------------------------- 1 | import DOMAttachedObject from 'vj/components/DOMAttachedObject'; 2 | import Editor from './index'; 3 | 4 | export default class TextareaHandler extends DOMAttachedObject { 5 | static DOMAttachKey = 'vjTextareaHandlerInstance'; 6 | 7 | getCmEditor() { 8 | return Editor.get(this.$dom); 9 | } 10 | 11 | isCmEditor() { 12 | const editor = this.getCmEditor(); 13 | return !!(editor && editor.isValid); 14 | } 15 | 16 | val(...argv) { 17 | if (this.isCmEditor()) { 18 | return this.getCmEditor().value(...argv); 19 | } 20 | return this.$dom.val(...argv); 21 | } 22 | 23 | focus() { 24 | if (this.isCmEditor()) { 25 | this.getCmEditor().focus(); 26 | } 27 | this.$dom.focus(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/ui-default/components/form/form.page.ts: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { AutoloadPage } from 'vj/misc/Page'; 3 | import { delay } from 'vj/utils'; 4 | 5 | const formPage = new AutoloadPage('formPage', () => { 6 | $(document).on('vjFormDisableUpdate', 'input, select, textarea', (ev) => { 7 | const $input = $(ev.currentTarget); 8 | const $formItem = $input.closest('.form__item'); 9 | $formItem[$input.prop('disabled') ? 'addClass' : 'removeClass']('is--disabled'); 10 | }); 11 | 12 | const submitting = {}; 13 | $(document).on('click', '[type="submit"]', (ev) => { 14 | if (submitting[ev.currentTarget]) ev.preventDefault(); 15 | submitting[ev.currentTarget] = true; 16 | delay(5000).then(() => { submitting[ev.currentTarget] = false; }); 17 | }); 18 | }); 19 | 20 | export default formPage; 21 | -------------------------------------------------------------------------------- /packages/ui-default/components/form/select.page.styl: -------------------------------------------------------------------------------- 1 | @import './var.inc.styl' 2 | 3 | .select 4 | form-styles() 5 | padding-right: 1.1rem 6 | background-color: $input-background-color 7 | background-image: url('~vj/misc/icons/expand_more.svg') 8 | background-size: 16px 16px 9 | background-position: right -1rem center 10 | background-origin: content-box 11 | background-repeat: no-repeat 12 | outline: $input-outline 13 | transition: outline-color .2s, border-color .2s 14 | transition-timing-function: ease-out-cubic 15 | 16 | &:focus 17 | border-color: $input-focus-border-color 18 | outline: $input-focus-outline 19 | outline-offset: 0 20 | 21 | &:disabled 22 | opacity: 0.5 23 | 24 | .select-container.compact .select, .select.compact 25 | margin-bottom: 0 26 | height: rem($compact-control-height) 27 | line-height: rem($compact-control-height - 2) 28 | padding-top: 0 29 | padding-bottom: 0 30 | -------------------------------------------------------------------------------- /packages/ui-default/components/form/textbox.page.ts: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { AutoloadPage } from 'vj/misc/Page'; 3 | 4 | const textboxPage = new AutoloadPage('textboxPage', () => { 5 | $(document).on('focusin', '.textbox.material input', (ev) => { 6 | $(ev.currentTarget).parent().addClass('focus'); 7 | }); 8 | 9 | $(document).on('focusout', '.textbox.material input', (ev) => { 10 | $(ev.currentTarget).parent().removeClass('focus'); 11 | }); 12 | 13 | const $focusElement = $(document.activeElement); 14 | if ($focusElement.prop('tagName') === 'INPUT' 15 | && $focusElement.parent().is('.textbox.material') 16 | ) { 17 | $focusElement.focusin(); 18 | } 19 | }); 20 | 21 | export default textboxPage; 22 | -------------------------------------------------------------------------------- /packages/ui-default/components/form/var.inc.styl: -------------------------------------------------------------------------------- 1 | form-styles() 2 | appearance: none 3 | display: block 4 | width: 100% 5 | font-size: rem($font-size-secondary) 6 | margin: $input-margin 7 | height: rem($form-control-height) 8 | line-height: 1.2 9 | padding: rem(5px) 10 | border: $input-border 11 | 12 | &.inline 13 | display: inline-block 14 | width: auto 15 | -------------------------------------------------------------------------------- /packages/ui-default/components/highlighter/meta.js: -------------------------------------------------------------------------------- 1 | // The following content is extracted from: 2 | // https://github.com/codemirror/CodeMirror/blob/master/mode/meta.js 3 | 4 | export default [ 5 | { name: 'C', ext: ['c', 'h'] }, 6 | { name: 'C++', ext: ['cpp', 'c++', 'cc', 'cxx', 'hpp', 'h++', 'hh', 'hxx'] }, 7 | { name: 'Go', ext: ['go'] }, 8 | { name: 'Haskell', ext: ['hs'] }, 9 | { name: 'Java', ext: ['java'] }, 10 | { name: 'JavaScript', ext: ['js'] }, 11 | { name: 'Pascal', ext: ['p', 'pas'] }, 12 | { name: 'PHP', ext: ['php', 'php3', 'php4', 'php5', 'php7', 'phtml'] }, 13 | { name: 'Python', ext: ['BUILD', 'bzl', 'py', 'pyw'] }, 14 | { name: 'Rust', ext: ['rs'] }, 15 | ]; 16 | -------------------------------------------------------------------------------- /packages/ui-default/components/hint.page.styl: -------------------------------------------------------------------------------- 1 | .hint 2 | font-size: $font-size-small 3 | position: relative 4 | top: $nav-item-height 5 | z-index: 99 6 | left: 0 7 | width: 100% 8 | color: #e58900 9 | background: #fff0e3; -------------------------------------------------------------------------------- /packages/ui-default/components/hint.ts: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { InfoDialog } from 'vj/components/dialog'; 3 | import { i18n, tpl } from 'vj/utils'; 4 | 5 | export default function createHint(message: string, element?: any) { 6 | if (i18n(message) === message || !element) return; 7 | const a = document.createElement('a'); 8 | a.setAttribute('href', 'javascript:;'); 9 | const span = document.createElement('span'); 10 | span.setAttribute('class', 'icon icon-help'); 11 | a.appendChild(span); 12 | a.onclick = () => { 13 | new InfoDialog({ 14 | cancelByClickingBack: false, 15 | $body: tpl.typoMsg(i18n(message), true), 16 | }).open(); 17 | }; 18 | $(element).append(a); 19 | } 20 | -------------------------------------------------------------------------------- /packages/ui-default/components/hitokoto/index.page.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { NamedPage } from 'vj/misc/Page'; 3 | import { i18n, request, tpl } from 'vj/utils'; 4 | 5 | export default new NamedPage('homepage', () => { 6 | function getHitokoto($containers) { 7 | $containers.get().forEach((container) => { 8 | request.get('https://v1.hitokoto.cn?c=a&c=b&c=c&c=d&c=e&c=f') 9 | .then((hitokoto) => { 10 | const dom = $(tpl`

${hitokoto.hitokoto}

`); 11 | dom.appendTo(container); 12 | }) 13 | .catch((e) => { 14 | console.error(e); 15 | const dom = $(tpl`

${i18n('Cannot get hitokoto.')}

`); 16 | dom.appendTo(container); 17 | }); 18 | }); 19 | } 20 | if ($('[name="hitokoto"]')) getHitokoto($('[name="hitokoto"]')); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/ui-default/components/katex/katex.page.styl: -------------------------------------------------------------------------------- 1 | .katex 2 | font-size: 1.2em 3 | -------------------------------------------------------------------------------- /packages/ui-default/components/loader/loader.page.styl: -------------------------------------------------------------------------------- 1 | .page-loader 2 | position: absolute 3 | top: 0 4 | left: 0 5 | right: 0 6 | bottom: 0 7 | display: flex 8 | align-items: center 9 | justify-content: center 10 | z-index: 9999 11 | 12 | .loader-container 13 | position: absolute 14 | left: 0 15 | width: 100% 16 | top: 0 17 | height: 100% 18 | 19 | .loader 20 | text-indent: -9999em 21 | border: rem(5px) solid rgba($primary-color, .2) 22 | border-left: rem(5px) solid $primary-color 23 | animation: load8 1.1s infinite linear 24 | border-radius: 50% 25 | width: rem(50px) 26 | height: rem(50px) 27 | margin-left: rem(-25px) 28 | margin-top: rem(-25px) 29 | left: 50% 30 | top: 50% 31 | position: absolute 32 | 33 | @keyframes load8 34 | 0% 35 | transform: rotate(0deg) 36 | 37 | 100% 38 | transform: rotate(360deg) 39 | 40 | 41 | -------------------------------------------------------------------------------- /packages/ui-default/components/marker/marker.page.js: -------------------------------------------------------------------------------- 1 | import { AutoloadPage } from 'vj/misc/Page'; 2 | import MarkerReactive from './MarkerReactive'; 3 | 4 | const markerPage = new AutoloadPage('markerPage', () => { 5 | MarkerReactive.initAll(); 6 | }); 7 | 8 | export default markerPage; 9 | -------------------------------------------------------------------------------- /packages/ui-default/components/menu/menu.page.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { AutoloadPage } from 'vj/misc/Page'; 3 | import { delay, slideDown } from 'vj/utils'; 4 | 5 | function expandMenu($menu) { 6 | slideDown($menu, 500, { opacity: 0 }, { opacity: 1 }); 7 | } 8 | 9 | async function expandAllMenus() { 10 | await delay(200); 11 | $('.menu.collapsed').get().forEach((menu) => expandMenu($(menu))); 12 | } 13 | 14 | const menuPage = new AutoloadPage('menuPage', () => { 15 | expandAllMenus(); 16 | }); 17 | 18 | export default menuPage; 19 | -------------------------------------------------------------------------------- /packages/ui-default/components/messagepad/DialogueListItem.page.styl: -------------------------------------------------------------------------------- 1 | $stripe-width = 2px 2 | 3 | .messagepad__list-item 4 | display: block 5 | padding: rem(20px $section-gap-h 20px ($section-gap-h - $stripe-width)) 6 | border-left: $stripe-width solid transparent 7 | cursor: pointer 8 | transition: border-color .1s linear, background-color .1s linear 9 | overflow: hidden 10 | text-overflow: ellipsis 11 | 12 | &:hover 13 | border-color: #DDD 14 | background: #F4F4F4 15 | text-decoration: none 16 | 17 | &.active 18 | border-color: $secondary-color 19 | background: rgba($secondary-color, 0.07) 20 | 21 | .messagepad__desc 22 | font-size: rem($font-size-small) 23 | color: #888 24 | max-height: 2em 25 | margin-top: rem(5px) 26 | overflow: hidden 27 | text-overflow: ellipsis 28 | word-wrap() 29 | -------------------------------------------------------------------------------- /packages/ui-default/components/messagepad/reducers/index.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import activeId from './activeId'; 3 | import dialogues from './dialogues'; 4 | import inputs from './inputs'; 5 | import isPosting from './isPosting'; 6 | 7 | const reducer = combineReducers({ 8 | activeId, 9 | dialogues, 10 | inputs, 11 | isPosting, 12 | }); 13 | 14 | export default reducer; 15 | -------------------------------------------------------------------------------- /packages/ui-default/components/monaco/languages/yaml.ts: -------------------------------------------------------------------------------- 1 | import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; 2 | import { configureMonacoYaml } from 'monaco-yaml'; 3 | import problemConfigSchema from '../schema/problemconfig'; 4 | 5 | configureMonacoYaml(monaco, { 6 | validate: true, 7 | enableSchemaRequest: true, 8 | hover: true, 9 | completion: true, 10 | format: false, 11 | schemas: [ 12 | { 13 | uri: 'https://hydro.js.org/schema/problemConfig.json', 14 | fileMatch: ['hydro://problem/file/config.yaml'], 15 | schema: problemConfigSchema as any, 16 | }, 17 | { 18 | uri: new URL('/manage/config/schema.json', window.location.href).toString(), 19 | fileMatch: ['hydro://system/setting.yaml'], 20 | }, 21 | ], 22 | }); 23 | -------------------------------------------------------------------------------- /packages/ui-default/components/navigation/nav-logo-small_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/navigation/nav-logo-small_dark.png -------------------------------------------------------------------------------- /packages/ui-default/components/notification/notification.page.js: -------------------------------------------------------------------------------- 1 | import Notification from 'vj/components/notification/index'; 2 | import { AutoloadPage } from 'vj/misc/Page'; 3 | import { i18n } from 'vj/utils'; 4 | 5 | export default new AutoloadPage('notificationPage', (pagename) => { 6 | const message = i18n(`Hint::Page::${pagename}`); 7 | const item = localStorage.getItem(`hint.${message}`); 8 | if (message !== `Hint::Page::${pagename}` && !item) { 9 | Notification.info(message, message.length * 500); 10 | localStorage.setItem(`hint.${message}`, true); 11 | } 12 | const text = new URL(window.location.href).searchParams.get('notification'); 13 | if (text) Notification.success(text); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/ui-default/components/problem/create.page.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { InfoDialog } from 'vj/components/dialog'; 3 | import { NamedPage } from 'vj/misc/Page'; 4 | import { i18n, tpl } from 'vj/utils'; 5 | 6 | export default new NamedPage(['problem_create', 'problem_edit'], () => { 7 | $('input[name="pid"]').on('blur', () => { 8 | if (/^[0-9]+$/.test($('input[name="pid"]').val())) { 9 | new InfoDialog({ 10 | $body: tpl.typoMsg(i18n('Problem ID cannot be a pure number. Leave blank if you want to use numberic id.')), 11 | }).open(); 12 | } 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/ui-default/components/problem/list.page.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { AutoloadPage } from 'vj/misc/Page'; 3 | 4 | export default new AutoloadPage('problemListPage', () => { 5 | $('.col--problem-name>a').attr('target', '_blank'); 6 | $(document).on('vjContentNew', () => { 7 | $('.col--problem-name>a').attr('target', '_blank'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/ui-default/components/problem/rp.page.styl: -------------------------------------------------------------------------------- 1 | .problem__rp-tag 2 | font-size: 0.8em 3 | padding: rem(0 5px) 4 | color: #AAA 5 | -------------------------------------------------------------------------------- /packages/ui-default/components/problemconfig/reducer/index.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import config from './config'; 3 | import testdata from './testdata'; 4 | 5 | const reducer = combineReducers({ 6 | config, 7 | testdata, 8 | }); 9 | 10 | export default reducer; 11 | export type RootState = ReturnType; 12 | -------------------------------------------------------------------------------- /packages/ui-default/components/problemconfig/reducer/testdata.ts: -------------------------------------------------------------------------------- 1 | export default function reducer(state = [], action: any = {}) { 2 | switch (action.type) { 3 | case 'CONFIG_LOAD_FULFILLED': { 4 | return state.concat(action.payload.testdata); 5 | } 6 | case 'CONFIG_DELETE_TESTDATA': { 7 | const next = state.filter((i) => i.name !== action.value[0]); 8 | return next; 9 | } 10 | case 'CONFIG_ADD_TESTDATA': { 11 | return state.concat([action.value]); 12 | } 13 | default: 14 | return state; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/1.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/10.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/11.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/12.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/13.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/14.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/15.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/16.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/17.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/18.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/19.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/2.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/20.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/21.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/3.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/4.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/5.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/6.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/7.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/8.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/9.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/gen_thumbnails.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for f in *.jpg 4 | do 5 | echo "$f..." 6 | convert "$f" -strip -interlace Plane -quality 92 -resize 200x124^ -gravity center -extent 200x124 "thumbnail/$f" 7 | done 8 | -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/1.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/10.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/11.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/12.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/13.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/14.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/15.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/16.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/17.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/18.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/19.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/2.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/20.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/21.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/3.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/4.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/5.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/6.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/7.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/8.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/backgrounds/thumbnail/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/components/profile/backgrounds/thumbnail/9.jpg -------------------------------------------------------------------------------- /packages/ui-default/components/profile/profile.page.styl: -------------------------------------------------------------------------------- 1 | .user-profile-avatar 2 | border-radius: 50% !important 3 | 4 | .user-profile-badge 5 | display: inline-block !important 6 | font-size: rem(12px) !important 7 | padding: rem(3px 4px) !important 8 | line-height: 1 !important 9 | 10 | &:hover 11 | text-decoration: none !important 12 | 13 | for n in (1..21) 14 | .user-profile-bg--{n} 15 | background-image: url('backgrounds/' + n + '.jpg') 16 | 17 | .user-profile-bg--thumbnail-{n} 18 | background-image: url('backgrounds/thumbnail/' + n + '.jpg') 19 | 20 | for key, _ in $badge-bg-color 21 | .badge--{key} 22 | background-color: $badge-bg-color[key] !important 23 | color: $badge-text-color[key] !important 24 | -------------------------------------------------------------------------------- /packages/ui-default/components/react/DomComponent.tsx: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { omit } from 'lodash'; 3 | import React from 'react'; 4 | 5 | export default function DomComponent(props: React.HTMLAttributes & { childDom: HTMLElement }) { 6 | const ref = React.useRef(null); 7 | React.useEffect(() => { 8 | ref.current.appendChild(props.childDom); 9 | return () => { 10 | $(ref.current).empty(); 11 | }; 12 | }, []); 13 | return
; 14 | } 15 | -------------------------------------------------------------------------------- /packages/ui-default/components/react/IconComponent.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import PropTypes from 'prop-types'; 3 | import React from 'react'; 4 | 5 | export default function IconComponent(props: { name: string, className?: string } & React.HTMLAttributes) { 6 | const { 7 | name, 8 | className, 9 | ...rest 10 | } = props; 11 | const cn = classNames(className, `icon icon-${name}`); 12 | return ( 13 | 14 | ); 15 | } 16 | 17 | IconComponent.propTypes = { 18 | name: PropTypes.string.isRequired, 19 | className: PropTypes.string, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/ui-default/components/record/record.page.styl: -------------------------------------------------------------------------------- 1 | .record-status--text 2 | for key, value in $record-status-color 3 | &.{key} 4 | color: value !important 5 | 6 | .record-status--icon 7 | display: inline-block 8 | width: 1.15em 9 | for key, value in $record-status-icon 10 | &.{key}:before 11 | content: value 12 | color: $record-status-color[key] 13 | 14 | .record-status--border 15 | border-left: rem(3px) solid transparent 16 | for key, value in $record-status-color 17 | &.{key} 18 | border-left: rem(3px) solid lighten(value, 10%) 19 | 20 | .record-status--background 21 | color: #FFF 22 | 23 | for key, value in $record-status-color 24 | &.{key} 25 | background: value 26 | 27 | -------------------------------------------------------------------------------- /packages/ui-default/components/rotator/rotator.page.styl: -------------------------------------------------------------------------------- 1 | .rotator 2 | position: relative 3 | height: 1em 4 | 5 | .rotator__item 6 | position: absolute 7 | left: 0 8 | top: 0 9 | width: 100% 10 | height: 100% 11 | transition: transform .2s, opacity .2s 12 | transition-timing-function: ease-out-cubic 13 | 14 | &.pos--above 15 | transform: translateY(-100%) 16 | opacity: 0 17 | 18 | &.pos--below 19 | transform: translateY(100%) 20 | opacity: 0 21 | -------------------------------------------------------------------------------- /packages/ui-default/components/scratchpad/DataInput.page.styl: -------------------------------------------------------------------------------- 1 | .scratchpad__data-input 2 | border: 0 3 | border-left: 3px solid #E0E0E0 4 | background: #F8F8F8 5 | padding: rem(8px 4px) 6 | font-family: $code-font-family 7 | font-size: rem($font-size-secondary) 8 | outline: 0 9 | resize: none 10 | color: #666 11 | height: 100% 12 | width: 100% 13 | transition: background-color .2s linear, border-color .2s linear 14 | 15 | &:focus 16 | background: #FFF 17 | border-color: lighten($primary-color, 70%) 18 | -------------------------------------------------------------------------------- /packages/ui-default/components/scratchpad/Editor.page.styl: -------------------------------------------------------------------------------- 1 | .ScratchpadMonacoEditor 2 | flex: 1 3 | position: relative 4 | 5 | .cm-s-vjcm 6 | position: absolute 7 | left: 0 8 | top: 0 9 | width: 100% 10 | height: 100% 11 | font-family: $code-font-family 12 | font-size: rem($font-size-secondary) 13 | line-height: 1.2 14 | -------------------------------------------------------------------------------- /packages/ui-default/components/scratchpad/Panel.page.styl: -------------------------------------------------------------------------------- 1 | @import './var.inc.styl' 2 | 3 | .scratchpad__panel-title 4 | background: $primary-color 5 | line-height: rem($panel-header-height) 6 | padding: rem(0 5px) 7 | font-size: rem($font-size-small) 8 | color: #FFF 9 | 10 | .splitpane-fill 11 | display: flex 12 | position: absolute 13 | overflow: hidden 14 | left: 0 15 | width: 100% 16 | top: 0 17 | height: 100% 18 | user-select: none 19 | -------------------------------------------------------------------------------- /packages/ui-default/components/scratchpad/PanelButton.page.styl: -------------------------------------------------------------------------------- 1 | @import './var.inc.styl' 2 | 3 | .scratchpad__panel-button 4 | background: none 5 | border: 0 6 | line-height: rem($panel-header-height) 7 | margin: 0 8 | color: #FFF 9 | padding: rem(0 5px) 10 | cursor: pointer 11 | font-size: rem($font-size-small) 12 | outline: 0 13 | 14 | &:hover 15 | color: $tab-color 16 | -------------------------------------------------------------------------------- /packages/ui-default/components/scratchpad/PanelButtonComponent.jsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import PropTypes from 'prop-types'; 3 | 4 | export default function PanelButtonComponent(props) { 5 | const { 6 | className, 7 | children, 8 | ...rest 9 | } = props; 10 | const cn = classNames(className, 'scratchpad__panel-button'); 11 | return ( 12 | 13 | ); 14 | } 15 | 16 | PanelButtonComponent.propTypes = { 17 | className: PropTypes.string, 18 | children: PropTypes.node, 19 | }; 20 | -------------------------------------------------------------------------------- /packages/ui-default/components/scratchpad/PanelComponent.jsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import PropTypes from 'prop-types'; 3 | import React from 'react'; 4 | 5 | export default function PanelComponent(props) { 6 | const { 7 | title, 8 | className, 9 | children, 10 | ...rest 11 | } = props; 12 | const cn = classNames(className, 'flex-col'); 13 | return ( 14 |
15 |
{title}
16 |
{children}
17 |
18 | ); 19 | } 20 | 21 | PanelComponent.propTypes = { 22 | title: PropTypes.node, 23 | className: PropTypes.string, 24 | children: PropTypes.node, 25 | }; 26 | -------------------------------------------------------------------------------- /packages/ui-default/components/scratchpad/Tab.page.styl: -------------------------------------------------------------------------------- 1 | .scratchpad__tablist 2 | user-select: none 3 | 4 | div 5 | display: flex 6 | width: 50px 7 | height: 50px 8 | font-size: 28px 9 | flex-direction: row 10 | align-items: center 11 | justify-content: center 12 | margin: rem(8px) 0 13 | fill: #666 14 | color: #666 15 | cursor: pointer 16 | &:hover 17 | fill: #000 18 | color: #000 19 | 20 | div.scratchpad__tab-active 21 | border-left: 4px solid #000 22 | fill: #000 23 | color: #000 24 | cursor: default 25 | -------------------------------------------------------------------------------- /packages/ui-default/components/scratchpad/reducers/editor.ts: -------------------------------------------------------------------------------- 1 | let cacheKey = `${UserContext._id}/${UiContext.pdoc.domainId}/${UiContext.pdoc.docId}`; 2 | if (UiContext.tdoc?._id) cacheKey += `@${UiContext.tdoc._id}`; 3 | 4 | // TODO switch to indexeddb 5 | export default function reducer(state = { 6 | lang: localStorage.getItem(`${cacheKey}#lang`) || UiContext.codeLang, 7 | code: localStorage.getItem(cacheKey) || UiContext.codeTemplate, 8 | }, action: any = {}) { 9 | if (action.type === 'SCRATCHPAD_EDITOR_UPDATE_CODE') { 10 | localStorage.setItem(cacheKey, action.payload); 11 | return { 12 | ...state, 13 | code: action.payload, 14 | }; 15 | } 16 | if (action.type === 'SCRATCHPAD_EDITOR_SET_LANG') { 17 | localStorage.setItem(`${cacheKey}#lang`, action.payload); 18 | return { 19 | ...state, 20 | lang: action.payload, 21 | }; 22 | } 23 | return state; 24 | } 25 | -------------------------------------------------------------------------------- /packages/ui-default/components/scratchpad/reducers/index.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import editor from './editor'; 3 | import pretest from './pretest'; 4 | import records from './records'; 5 | import state from './state'; 6 | import ui from './ui'; 7 | 8 | const reducer = combineReducers({ 9 | ui, 10 | editor, 11 | pretest, 12 | records, 13 | state, 14 | }); 15 | 16 | export default reducer; 17 | export type RootState = ReturnType; 18 | -------------------------------------------------------------------------------- /packages/ui-default/components/scratchpad/reducers/state.ts: -------------------------------------------------------------------------------- 1 | export default function reducer(state = {}, action: any = {}) { 2 | if (action.type === 'SCRATCHPAD_STATE_UPDATE') { 3 | const { key, value } = action.payload; 4 | return { 5 | ...state, 6 | [key]: value, 7 | }; 8 | } 9 | return state; 10 | } 11 | -------------------------------------------------------------------------------- /packages/ui-default/components/scratchpad/var.inc.styl: -------------------------------------------------------------------------------- 1 | $panel-header-height = 34px 2 | $tab-color = #fff170 3 | -------------------------------------------------------------------------------- /packages/ui-default/components/star/star.page.styl: -------------------------------------------------------------------------------- 1 | .star 2 | border: 0 3 | background: none 4 | outline: 0 5 | padding: 0 6 | margin: 0 7 | color: gray 8 | 9 | .starred--show 10 | display: none 11 | .starred--hide 12 | display: block 13 | 14 | &.activated 15 | color: orange 16 | .starred--show 17 | display: block 18 | .starred--hide 19 | display: none 20 | 21 | -------------------------------------------------------------------------------- /packages/ui-default/components/tab/tab.page.js: -------------------------------------------------------------------------------- 1 | import { AutoloadPage } from 'vj/misc/Page'; 2 | import Tab from './Tab'; 3 | 4 | const tabPage = new AutoloadPage('tabPage', () => { 5 | Tab.initAll(); 6 | Tab.initEventListeners(); 7 | }); 8 | 9 | export default tabPage; 10 | -------------------------------------------------------------------------------- /packages/ui-default/components/tab/var.inc.styl: -------------------------------------------------------------------------------- 1 | $tab-item-height = 35px 2 | $tab-item-shadow-size = 10px 3 | $tab-item-height-mobile = 32px // 32 * (13 / 16) is an integer 4 | -------------------------------------------------------------------------------- /packages/ui-default/components/table/styledTable.page.js: -------------------------------------------------------------------------------- 1 | import { AutoloadPage } from 'vj/misc/Page'; 2 | import StyledTable from './StyledTable'; 3 | 4 | const styledTablePage = new AutoloadPage('styledTablePage', () => { 5 | StyledTable.registerLifeCycleHooks(); 6 | }); 7 | 8 | export default styledTablePage; 9 | -------------------------------------------------------------------------------- /packages/ui-default/components/training/training.page.styl: -------------------------------------------------------------------------------- 1 | .training-status--icon 2 | display: inline-block 3 | width: 1.15em 4 | for key, value in $training-status-icon 5 | &.{key}:before 6 | content: value 7 | color: $training-status-color[key] 8 | 9 | .training-status--text 10 | for key, value in $training-status-color 11 | &.{key} 12 | color: value 13 | 14 | .training-section-status--icon 15 | display: inline-block 16 | width: 1.15em 17 | for key, value in $training-section-status-icon 18 | &.{key}:before 19 | content: value 20 | color: $training-status-color[key] 21 | 22 | .training-section-status--text 23 | for key, value in $training-section-status-color 24 | &.{key} 25 | color: value 26 | -------------------------------------------------------------------------------- /packages/ui-default/constant/domain.js: -------------------------------------------------------------------------------- 1 | export const JOIN_METHOD_NONE = 0; 2 | export const JOIN_METHOD_ALL = 1; 3 | export const JOIN_METHOD_CODE = 2; 4 | export const JOIN_METHOD_RANGE = { 5 | [JOIN_METHOD_NONE]: 'No user is allowed to join this domain', 6 | [JOIN_METHOD_ALL]: 'Any user is allowed to join this domain', 7 | [JOIN_METHOD_CODE]: 'Any user is allowed to join this domain with an invitation code', 8 | }; 9 | 10 | export const JOIN_EXPIRATION_KEEP_CURRENT = 0; 11 | export const JOIN_EXPIRATION_UNLIMITED = -1; 12 | 13 | export const JOIN_EXPIRATION_RANGE = { 14 | [JOIN_EXPIRATION_KEEP_CURRENT]: 'Keep current expiration', 15 | 3: 'In 3 hours', 16 | 24: 'In 1 day', 17 | [24 * 3]: 'In 3 days', 18 | [24 * 7]: 'In 1 week', 19 | [24 * 30]: 'In 1 month', 20 | [JOIN_EXPIRATION_UNLIMITED]: 'Never expire', 21 | }; 22 | -------------------------------------------------------------------------------- /packages/ui-default/constant/message.js: -------------------------------------------------------------------------------- 1 | export const FLAG_UNREAD = 1; 2 | export const FLAG_ALERT = 2; 3 | export const FLAG_RICHTEXT = 4; 4 | export const FLAG_INFO = 8; 5 | export const FLAG_I18N = 16; 6 | -------------------------------------------------------------------------------- /packages/ui-default/context.ts: -------------------------------------------------------------------------------- 1 | import * as cordis from 'cordis'; 2 | import type { EventMap } from './api'; 3 | 4 | export interface Events extends cordis.Events, EventMap { } 5 | 6 | export type { Disposable, FiberState, Plugin } from 'cordis'; 7 | 8 | export interface Context { 9 | [Context.events]: Events; 10 | broadcast: Context['emit']; 11 | } 12 | 13 | export class Context extends cordis.Context { } 14 | export type Fiber = cordis.Fiber; 15 | 16 | export class Service extends cordis.Service { 17 | } 18 | export const ctx = new Context(); 19 | -------------------------------------------------------------------------------- /packages/ui-default/misc/float.styl: -------------------------------------------------------------------------------- 1 | .float-left 2 | float: left 3 | 4 | .float-right 5 | float: right 6 | 7 | .clearfix:after 8 | visibility: hidden 9 | display: block 10 | font-size: 0 11 | content: ' ' 12 | clear: both 13 | height: 0 14 | zoom: 1 15 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/account--circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/add.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/award.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/block.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/bold.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/book.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/calendar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/check--circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/check.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/chevron_left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/chevron_right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/close--circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/code.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/comment--multiple.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/comment--text.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/copy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/crown.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/debug.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/download.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/emoji.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/enlarge.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/erase.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/expand_less.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/expand_more.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/facebook.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/feeling-lucky.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/filter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/flag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/formula.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/global.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/google_plus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/heart--outline.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/heart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/help.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/help2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/homework.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/hourglass.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/info--circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/info.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/insert--image.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/insert--link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/italic.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/lab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/link--external.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/linkedin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/logout.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/mail.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/ordered_list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/pie-chart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/platform--android.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/platform--chromeos.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/platform--ios.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/platform--mac.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/platform--mobile.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/platform--unknown.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/platform--windows.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/preview.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/qq.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/quote.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/reply.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/schedule--fill.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/schedule.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/security.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/send.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/settings.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/shrink.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/sliders.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/stack.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/star--outline.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/statistics.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/stopwatch.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/tag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/template/webicon.inc.styl: -------------------------------------------------------------------------------- 1 | $icon-font-name = 'hydro-icons' 2 | 3 | {% for name, value in infoData %} 4 | ${{prefix}}-{{ name }} = '{{ value.encodedCode }}' 5 | {%- endfor %} 6 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/template/webicon.styl: -------------------------------------------------------------------------------- 1 | @font-face 2 | font-family: $icon-font-name 3 | src: url('./hydro-icons.eot') 4 | src: url('./hydro-icons.eot#iefix') format('embedded-opentype'), 5 | url('./hydro-icons.woff2') format('woff2'), 6 | url('./hydro-icons.woff') format('woff'), 7 | url('./hydro-icons.ttf') format('truetype') 8 | font-weight: normal 9 | font-style: normal 10 | 11 | .icon 12 | font-family: $icon-font-name !important;{{fontSize}} 13 | speak: none 14 | font-style: normal 15 | font-weight: normal 16 | font-variant: normal 17 | text-transform: none 18 | line-height: 1 19 | -webkit-font-smoothing: antialiased 20 | -moz-osx-font-smoothing: grayscale 21 | 22 | {{cssString}} 23 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/unordered_list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/upload.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/usb.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/user--multiple.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/vote--down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/vote--up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/warning.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/web.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/wechat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/icons/wrench.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui-default/misc/immersive-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/misc/immersive-background.jpg -------------------------------------------------------------------------------- /packages/ui-default/misc/immersive-background@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/misc/immersive-background@2x.jpg -------------------------------------------------------------------------------- /packages/ui-default/misc/immersive.styl: -------------------------------------------------------------------------------- 1 | .layout--immersive 2 | #panel 3 | background: #80b2d5 url('immersive-background.jpg') no-repeat 4 | background-size: 1920px 1080px 5 | background-size: cover 6 | +retina() 7 | background-image: url('immersive-background@2x.jpg') 8 | 9 | .immersive--content 10 | color: $immersive-text-color 11 | 12 | h1, h2, h3, h4, h5, h6 13 | color: $immersive-header-color 14 | 15 | +above(rupture.mobile-cutoff) 16 | .immersive--center 17 | width: 270px 18 | margin: 0 auto 19 | 20 | .immersive--center 21 | h1 22 | text-align: center 23 | margin: 2em 0 24 | -------------------------------------------------------------------------------- /packages/ui-default/misc/nothing.styl: -------------------------------------------------------------------------------- 1 | .nothing-placeholder 2 | padding: rem(80px 0) 3 | text-align: center 4 | font-size: rem($font-size-title) 5 | color: #AAA 6 | 7 | &.compact 8 | padding: rem(20px 0) 9 | 10 | .nothing-icon 11 | background: url(./puzzled_twd2.svg) no-repeat center center 12 | height: 184px 13 | margin-bottom: rem(20px) 14 | -------------------------------------------------------------------------------- /packages/ui-default/misc/slideout.styl: -------------------------------------------------------------------------------- 1 | html 2 | height: 100% 3 | 4 | body 5 | width: 100% 6 | 7 | body, #panel 8 | min-height: 100% 9 | min-height: 100vh 10 | 11 | .slideout-panel 12 | position: relative 13 | z-index: 1 14 | 15 | .slideout-overlay 16 | display: none 17 | position: absolute 18 | left: 0 19 | top: 0 20 | width: 100% 21 | height: 100% 22 | z-index: 99999 23 | 24 | +mobile() 25 | .nav.slideout-menu 26 | position: fixed 27 | top: 0 28 | bottom: 0 29 | right: 0 30 | left: auto 31 | z-index: 0 32 | width: 200px 33 | overflow-y: auto 34 | -webkit-overflow-scrolling: touch 35 | display: none 36 | height: auto 37 | min-height: 100vh 38 | 39 | .slideout-open, 40 | .slideout-open body, 41 | .slideout-open .slideout-panel 42 | overflow: hidden 43 | 44 | .slideout-open .slideout-menu 45 | display: block !important 46 | -------------------------------------------------------------------------------- /packages/ui-default/misc/textalign.styl: -------------------------------------------------------------------------------- 1 | .text-center 2 | text-align: center 3 | 4 | .text-right 5 | text-align: right 6 | 7 | .text-left 8 | text-align: left 9 | 10 | .text-justify 11 | text-align: justify 12 | -------------------------------------------------------------------------------- /packages/ui-default/pages/api.page.styl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/pages/api.page.styl -------------------------------------------------------------------------------- /packages/ui-default/pages/contest_main.page.ts: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { NamedPage } from 'vj/misc/Page'; 3 | 4 | const page = new NamedPage('contest_main', () => { 5 | $('[name="filter-form"] [name="rule"]').on('change', () => { 6 | $('[name="filter-form"]').trigger('submit'); 7 | }); 8 | }); 9 | 10 | export default page; 11 | -------------------------------------------------------------------------------- /packages/ui-default/pages/discussion_detail.page.styl: -------------------------------------------------------------------------------- 1 | .page--discussion_detail, 2 | .page--blog_detail 3 | 4 | .topic__content 5 | margin: rem(20px 0) 6 | 7 | .profile__bg 8 | background-size: cover 9 | background-position: center center 10 | height: rem(60px) 11 | margin-bottom: rem(-32px) 12 | 13 | .sidebar-user-profile 14 | .media__body 15 | padding-left: rem(10px) 16 | padding-top: rem(20px) 17 | 18 | .user-profile-name 19 | font-size: 1.2rem 20 | color: #444 21 | vertical-align: middle 22 | -------------------------------------------------------------------------------- /packages/ui-default/pages/discussion_node_bg/nodes@2x_advice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/pages/discussion_node_bg/nodes@2x_advice.png -------------------------------------------------------------------------------- /packages/ui-default/pages/discussion_node_bg/nodes@2x_qa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/pages/discussion_node_bg/nodes@2x_qa.png -------------------------------------------------------------------------------- /packages/ui-default/pages/discussion_node_bg/nodes@2x_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/pages/discussion_node_bg/nodes@2x_share.png -------------------------------------------------------------------------------- /packages/ui-default/pages/discussion_node_bg/nodes@2x_solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/pages/discussion_node_bg/nodes@2x_solution.png -------------------------------------------------------------------------------- /packages/ui-default/pages/discussion_node_bg/nodes@2x_vijos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/pages/discussion_node_bg/nodes@2x_vijos.png -------------------------------------------------------------------------------- /packages/ui-default/pages/discussion_node_bg/nodes_advice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/pages/discussion_node_bg/nodes_advice.png -------------------------------------------------------------------------------- /packages/ui-default/pages/discussion_node_bg/nodes_qa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/pages/discussion_node_bg/nodes_qa.png -------------------------------------------------------------------------------- /packages/ui-default/pages/discussion_node_bg/nodes_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/pages/discussion_node_bg/nodes_share.png -------------------------------------------------------------------------------- /packages/ui-default/pages/discussion_node_bg/nodes_solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/pages/discussion_node_bg/nodes_solution.png -------------------------------------------------------------------------------- /packages/ui-default/pages/discussion_node_bg/nodes_vijos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/pages/discussion_node_bg/nodes_vijos.png -------------------------------------------------------------------------------- /packages/ui-default/pages/domain_group.page.styl: -------------------------------------------------------------------------------- 1 | .page--domain_group 2 | .col--checkbox 3 | width: 60px 4 | 5 | .col--group 6 | width: 150px 7 | -------------------------------------------------------------------------------- /packages/ui-default/pages/domain_join.page.styl: -------------------------------------------------------------------------------- 1 | .page--domain_join 2 | .domain_join__container 3 | +above(rupture.mobile-cutoff) 4 | width: rem(500px) 5 | margin: rem(50px) auto 6 | 7 | +above(rupture.desktop-cutoff) 8 | width: rem(700px) 9 | -------------------------------------------------------------------------------- /packages/ui-default/pages/domain_join_applications.page.ts: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import * as domainEnum from 'vj/constant/domain'; 3 | import { NamedPage } from 'vj/misc/Page'; 4 | 5 | const page = new NamedPage('domain_join_applications', () => { 6 | const $role = $('[name="role"]'); 7 | const $expire = $('[name="expire"]'); 8 | const $code = $('[name="invitationCode"]'); 9 | function updateFormState() { 10 | const method = +$('[name="method"]').val(); 11 | $role.prop('disabled', method === domainEnum.JOIN_METHOD_NONE).trigger('vjFormDisableUpdate'); 12 | $expire.prop('disabled', method === domainEnum.JOIN_METHOD_NONE).trigger('vjFormDisableUpdate'); 13 | $code.prop('disabled', method !== domainEnum.JOIN_METHOD_CODE).trigger('vjFormDisableUpdate'); 14 | } 15 | updateFormState(); 16 | $('[name="method"]').on('change', updateFormState); 17 | }); 18 | 19 | export default page; 20 | -------------------------------------------------------------------------------- /packages/ui-default/pages/domain_permission.page.styl: -------------------------------------------------------------------------------- 1 | .page--domain_permission 2 | .col--p 3 | width: 80px 4 | text-align: center 5 | border-left: 1px solid $table-border-color 6 | 7 | td.col--description 8 | padding-left: rem(30px) !important 9 | 10 | .col--family 11 | line-height: 2 12 | color: #FFF 13 | background: lighten(saturate($primary-color, 70%), 30%) 14 | border-top: 2px solid $primary-color 15 | -------------------------------------------------------------------------------- /packages/ui-default/pages/domain_role.page.styl: -------------------------------------------------------------------------------- 1 | .page--domain_role 2 | .col--checkbox 3 | width: 60px 4 | 5 | .col--users 6 | width: 100px 7 | -------------------------------------------------------------------------------- /packages/ui-default/pages/domain_user.page.styl: -------------------------------------------------------------------------------- 1 | .page--domain_user 2 | .col--checkbox 3 | width: 60px 4 | 5 | .col--uid 6 | width: 100px 7 | 8 | .col--role 9 | width: 160px 10 | -------------------------------------------------------------------------------- /packages/ui-default/pages/files.page.styl: -------------------------------------------------------------------------------- 1 | .page--home_files, .page--training_files, .page--contest_detail, .page--contest_manage, .page--homework_files 2 | .col--checkbox 3 | width: 60px 4 | 5 | .col--size 6 | width: 100px 7 | 8 | .col--operation 9 | width: 40px 10 | -------------------------------------------------------------------------------- /packages/ui-default/pages/home_account.page.styl: -------------------------------------------------------------------------------- 1 | .page--home_account 2 | [name="form_item_background_img"] .radiobox__image 3 | width: rem(100px) 4 | height: rem(62px) 5 | background-size: rem(100px 62px) 6 | -------------------------------------------------------------------------------- /packages/ui-default/pages/home_domain.page.styl: -------------------------------------------------------------------------------- 1 | .page--home_domain 2 | .col--icon 3 | width: 60px 4 | line-height: 1 5 | 6 | img 7 | border-radius: 50% 8 | 9 | .col--role 10 | width: rem(160px) 11 | border-left: 1px solid $table-border-color 12 | 13 | .col--action 14 | width: rem(200px) 15 | 16 | +mobile() 17 | .col--icon, .col--role 18 | display: none 19 | -------------------------------------------------------------------------------- /packages/ui-default/pages/homework_detail.page.styl: -------------------------------------------------------------------------------- 1 | .page--homework_detail 2 | .col--status 3 | width: rem(150px) 4 | 5 | .col--submit-at 6 | width: rem(150px) 7 | 8 | .col--problem 9 | border-left: 1px solid $table-border-color 10 | 11 | +mobile() 12 | .col--submit-at 13 | display: none -------------------------------------------------------------------------------- /packages/ui-default/pages/homework_edit.page.ts: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { ConfirmDialog } from 'vj/components/dialog'; 3 | import { NamedPage } from 'vj/misc/Page'; 4 | import { i18n, request, tpl } from 'vj/utils'; 5 | 6 | export default new NamedPage('homework_edit', () => { 7 | let confirmed = false; 8 | $(document).on('click', '[value="delete"]', (ev) => { 9 | ev.preventDefault(); 10 | if (confirmed) { 11 | return request.post('', { operation: 'delete' }).then((res) => { 12 | window.location.href = res.url; 13 | }); 14 | } 15 | const message = 'Confirm deleting this homework? Its files and status will be deleted as well.'; 16 | return new ConfirmDialog({ 17 | $body: tpl.typoMsg(i18n(message)), 18 | }).open().then((action) => { 19 | if (action !== 'yes') return; 20 | confirmed = true; 21 | ev.target.click(); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/ui-default/pages/homework_main.page.styl: -------------------------------------------------------------------------------- 1 | $highlight-button-color = #F6DF45 2 | 3 | .page--homework_main 4 | .homework__title 5 | line-height: 1.2em 6 | margin-bottom: rem(10px) 7 | font-size: rem($font-size-title) 8 | 9 | .homework__info-attended 10 | color: $success-color 11 | 12 | .homework__date 13 | margin-right: rem(10px) 14 | white-space: nowrap 15 | 16 | .filter-form 17 | display: inline-block -------------------------------------------------------------------------------- /packages/ui-default/pages/homework_scoreboard.page.styl: -------------------------------------------------------------------------------- 1 | .page--homework_scoreboard 2 | .main 3 | .row 4 | max-width: 100vw !important; 5 | 6 | .col--rank 7 | width: rem(60px) 8 | text-align: center 9 | border-right: 1px solid $table-border-color 10 | 11 | .col--user 12 | min-width: rem(150px) 13 | 14 | .col--total_score, .col--time 15 | width: rem(100px) 16 | text-align: center 17 | 18 | .col--problem, .col--record 19 | width: rem(100px) 20 | border-left: 1px solid $table-border-color 21 | text-align: center 22 | 23 | +mobile() 24 | .col--problem, .col--record 25 | display: none 26 | -------------------------------------------------------------------------------- /packages/ui-default/pages/manage_user_priv.page.styl: -------------------------------------------------------------------------------- 1 | .page--manage_user_priv 2 | .col--uid 3 | width: 100px 4 | 5 | .col--priv 6 | width: 100px 7 | 8 | .col--actions 9 | width: 100px 10 | 11 | .priv_plus 12 | color: green 13 | 14 | .priv_minus 15 | color: red 16 | 17 | .col--p 18 | width: 30px 19 | -------------------------------------------------------------------------------- /packages/ui-default/pages/problem_detail.page.styl: -------------------------------------------------------------------------------- 1 | .page--problem_detail, 2 | .page--contest_detail_problem, 3 | .page--homework_detail_problem 4 | .section__tab-header 5 | padding-top: 0; 6 | 7 | .section__tab-container + .section__body 8 | padding-top: 0 9 | 10 | .section__tab-header-item 11 | border-top: 3px solid transparent; 12 | transition: border-color 0.2s, background 0.2s, color 0.2s; 13 | transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 14 | margin-right: 0; 15 | color: #182026 !important; 16 | text-decoration: none; 17 | 18 | &:hover 19 | border-color: #5f9fd6; 20 | background: #f8f8f8; 21 | 22 | &.tab--active 23 | border-color: #ed5f82; -------------------------------------------------------------------------------- /packages/ui-default/pages/problem_edit.page.styl: -------------------------------------------------------------------------------- 1 | .page--problem_edit,.page--problem_create 2 | .problem-sidebar-additional_file 3 | height: 270px 4 | overflow-y: auto 5 | 6 | .col--size 7 | width: 80px 8 | .col--operation 9 | width: 60px 10 | text-align: center -------------------------------------------------------------------------------- /packages/ui-default/pages/problem_files.page.styl: -------------------------------------------------------------------------------- 1 | .page--problem_files 2 | .col--checkbox 3 | width: 60px 4 | 5 | .col--size 6 | width: 120px 7 | 8 | .col--operation 9 | width: 60px 10 | text-align: center 11 | 12 | .rename-confirm-table 13 | x-overflow: auto 14 | 15 | .col--origin, .col--new 16 | width: 50% 17 | text-align: center -------------------------------------------------------------------------------- /packages/ui-default/pages/problem_statistics.page.styl: -------------------------------------------------------------------------------- 1 | .page--problem_statistics 2 | .col--status 3 | width: rem(80px) 4 | 5 | .col--submit-at 6 | width: rem(160px) 7 | text-align: center 8 | 9 | .col--time, .col--memory, .col--lang, .col--code 10 | width: rem(85px) 11 | text-align: center 12 | 13 | .col--status, .col--submit-by, .col--code 14 | border-right: 1px solid $table-border-color -------------------------------------------------------------------------------- /packages/ui-default/pages/problem_submit.page.styl: -------------------------------------------------------------------------------- 1 | .page--problem_submit 2 | .col--status 3 | width: rem(150px) 4 | 5 | .col--submit-at 6 | width: rem(160px) 7 | text-align: center 8 | 9 | +mobile() 10 | .col--memory, 11 | .col--time 12 | display: none 13 | -------------------------------------------------------------------------------- /packages/ui-default/pages/ranking.page.styl: -------------------------------------------------------------------------------- 1 | .page--ranking, .page--homepage 2 | .col--rank 3 | width: rem(65px) 4 | position: relative 5 | 6 | .col--user 7 | width: rem(280px) 8 | 9 | .col--rp 10 | width: rem(60px) 11 | text-align: right 12 | 13 | .col--detail 14 | width: rem(50px) 15 | text-align: center 16 | 17 | &[lang="en"] 18 | .col--detail 19 | width: rem(75px) 20 | 21 | .col--ac 22 | width: rem(50px) 23 | text-align: center 24 | 25 | &[lang="en"] 26 | .col--ac 27 | width: rem(70px) 28 | 29 | .col--bio 30 | display: block; 31 | 32 | td.col--bio 33 | height: 36px !important; 34 | 35 | +mobile() 36 | .col--bio, .col--detail 37 | display: none 38 | -------------------------------------------------------------------------------- /packages/ui-default/pages/status.page.styl: -------------------------------------------------------------------------------- 1 | .page--status 2 | .col--id 3 | width: 100px 4 | 5 | .col--os 6 | width: 200px 7 | 8 | .col--status 9 | width: 130px 10 | 11 | .col--memory 12 | width: 150px 13 | 14 | .col--request 15 | width: 130px 16 | 17 | +mobile() 18 | .col--os 19 | .col--cpu 20 | display: none -------------------------------------------------------------------------------- /packages/ui-default/pages/training_edit.page.ts: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { ConfirmDialog } from 'vj/components/dialog'; 3 | import { NamedPage } from 'vj/misc/Page'; 4 | import { i18n, request, tpl } from 'vj/utils'; 5 | 6 | export default new NamedPage('training_edit', () => { 7 | let confirmed = false; 8 | $(document).on('click', '[name="operation"]', (ev) => { 9 | ev.preventDefault(); 10 | if (confirmed) { 11 | return request.post('.', { operation: 'delete' }).then((res) => { 12 | window.location.href = res.url; 13 | }); 14 | } 15 | const message = 'Confirm deleting this training? Its files and status will be deleted as well.'; 16 | return new ConfirmDialog({ 17 | $body: tpl.typoMsg(i18n(message)), 18 | }).open().then((action) => { 19 | if (action !== 'yes') return; 20 | confirmed = true; 21 | ev.target.click(); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/ui-default/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /packages/ui-default/static/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/static/android-chrome-192x192.png -------------------------------------------------------------------------------- /packages/ui-default/static/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/static/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /packages/ui-default/static/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/static/favicon-16x16.png -------------------------------------------------------------------------------- /packages/ui-default/static/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/static/favicon-32x32.png -------------------------------------------------------------------------------- /packages/ui-default/static/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/static/favicon-96x96.png -------------------------------------------------------------------------------- /packages/ui-default/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/static/favicon.ico -------------------------------------------------------------------------------- /packages/ui-default/static/img/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/static/img/avatar.png -------------------------------------------------------------------------------- /packages/ui-default/static/img/team_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hydro-dev/Hydro/7968a10d4930a9f37f059e6da4cd42873121d2a3/packages/ui-default/static/img/team_avatar.png -------------------------------------------------------------------------------- /packages/ui-default/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /problem/ 3 | Disallow: /d/*/problem/ 4 | Disallow: /record 5 | Disallow: /record/ 6 | Disallow: /d/*/record 7 | Disallow: /d/*/record/ 8 | Disallow: /manage/ 9 | Disallow: /domain/ 10 | Disallow: /d/*/domain/ 11 | Disallow: /home/ 12 | Disallow: /d/*/home/ 13 | Disallow: /d/*/user/ 14 | Disallow: /p/*/submit 15 | Disallow: /d/*/p/*/submit 16 | Disallow: /p/*/files 17 | Disallow: /d/*/p/*/files 18 | Disallow: /p/*/edit 19 | Disallow: /d/*/p/*/edit 20 | Disallow: /p/*/file/ 21 | Disallow: /d/*/p/*/file/ 22 | -------------------------------------------------------------------------------- /packages/ui-default/templates/about.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/wiki_base.html" %} 2 | {% block wiki_content %} 3 | {% set name = handler.domain.ui.name|default(model.system.get('server.name')) %} 4 | {%- for section in sections -%} 5 |
6 |
7 |

{{ section.title }}

8 |
9 |
10 | {{ section.content|markdown|safe }} 11 |
12 |
13 | {%- endfor -%} 14 | {% endblock %} -------------------------------------------------------------------------------- /packages/ui-default/templates/components/contest.html: -------------------------------------------------------------------------------- 1 | {% import "components/user.html" as user with context %} 2 | {% macro render_time(tdoc_time) %} 3 | {{ datetimeSpan(tdoc_time, false, 'YYYY-M-D H:mm')|safe }} 4 | {% endmacro %} 5 | 6 | {% macro render_duration(tdoc) %} 7 | {{ tdoc.duration|round(1) if tdoc.duration else ((tdoc.endAt.getTime() - tdoc.beginAt.getTime()) /1000 / 3600)|round(1) }} 8 | {% endmacro %} 9 | 10 | {% macro render_clarification_subject(tdoc, pdict, subject) %} 11 | {% if subject == 0 %}{{ _('General Issue') }}{% elif subject == -1 %}{{ _('Technical Issue') }}{% else %}{{ String.fromCharCode(65 + tdoc.pids.indexOf(subject)) + 1 }}. {{ pdict[subject].title }}{% endif %} 12 | {% endmacro %} -------------------------------------------------------------------------------- /packages/ui-default/templates/components/homework.html: -------------------------------------------------------------------------------- 1 | {% import "components/user.html" as user with context %} 2 | {% macro render_time(tdoc_time) %} 3 | {{ datetimeSpan(tdoc_time, false, 'YYYY-M-D H:mm')|safe }} 4 | {% endmacro %} 5 | 6 | {% macro render_extension(tdoc) %} 7 | {{ ((tdoc['endAt'].getTime() - tdoc['penaltySince'].getTime())/ 1000 / 3600)|round(1) }} 8 | {% endmacro %} 9 | -------------------------------------------------------------------------------- /packages/ui-default/templates/components/noscript_note.html: -------------------------------------------------------------------------------- 1 | {% macro render() %} 2 | {% if not isIE(handler.request.headers['user-agent']) and not handler.session.legacy %} 3 | 8 | {% else %} 9 |
10 |
11 |

{{ _('This page needs JavaScript to work.') }}

12 |
13 |
14 | {% endif %} 15 | {% endmacro %} 16 | -------------------------------------------------------------------------------- /packages/ui-default/templates/components/nothing.html: -------------------------------------------------------------------------------- 1 | {% macro render(text, compact=false, args=[]) %} 2 |
3 |
4 | {{ _(text).format(args) }} 5 |
6 | {% endmacro %} 7 | -------------------------------------------------------------------------------- /packages/ui-default/templates/components/sidemenu.html: -------------------------------------------------------------------------------- 1 | {% macro render_item(icon_name, page_name, args={}, label=none, menu_item=none) %} 2 | {% set target_url = url(page_name, args) if page_name else '#' %} 3 | 11 | {% endmacro %} 12 | -------------------------------------------------------------------------------- /packages/ui-default/templates/domain_edit.html: -------------------------------------------------------------------------------- 1 | {% extends "domain_base.html" %} 2 | {% block domain_content %} 3 |
4 | {% include "partials/setting.html" %} 5 |
6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /packages/ui-default/templates/domain_user_raw.html: -------------------------------------------------------------------------------- 1 |
2 | {%- for role in roles -%}{%- for rudoc in rudocs[role._id] -%}
3 | {{ rudoc._id }},{{ rudoc.role }},{{ udict[rudoc._id].uname }},{{ udict[rudoc._id].displayName}}
4 | {% endfor -%}{%- endfor -%}
5 | 
-------------------------------------------------------------------------------- /packages/ui-default/templates/error.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/basic.html" %} 2 | {% block content %} 3 |
4 |
5 |
6 |
7 |
8 |

{{ _('Oops!') }}

9 |

{{ _(error.message).formatFromArray(error.params) }}

10 |

{{ _('Technical Information') }}:

11 |

{{ _('Type') }}: {{ error.name }}

12 |

{{ _('Arguments') }}: 13 |

    14 | {%- for param in error.params -%} 15 | {% if instanceof(param, UserFacingError) %} 16 |
  1. {{ _(param.message).formatFromArray(param.params)}}
  2. 17 | {% else %} 18 |
  3. {{ param }}
  4. 19 | {% endif %} 20 | {%- endfor -%} 21 |
22 |

23 |
24 |
25 | {% endblock %} 26 | -------------------------------------------------------------------------------- /packages/ui-default/templates/home_files.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/home_base.html" %} 2 | {% block home_content %} 3 |
4 |
5 |

{{ _('Files') }}

6 |
7 | 8 |
9 |
10 | {{ noscript_note.render() }} 11 | {% include "partials/files.html" %} 12 |
13 | 14 |
15 |
16 | {% endblock %} -------------------------------------------------------------------------------- /packages/ui-default/templates/home_messages.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/home_base.html" %} 2 | {% block home_content %} 3 |
4 |
5 | {{ noscript_note.render() }} 6 |
7 |
8 |
9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /packages/ui-default/templates/homework_files.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/basic.html" %} 2 | {% block content %} 3 | {{ set(UiContext, 'tdoc', tdoc) }} 4 |
5 |
6 |
7 |
8 |

{{ _('Files') }}

9 |
10 | 11 |
12 |
13 | {{ noscript_note.render() }} 14 | {% include "partials/files.html" %} 15 |
16 | 17 |
18 |
19 |
20 |
21 | {% include "partials/homework_sidebar.html" %} 22 |
23 |
24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /packages/ui-default/templates/layout/home_base.html: -------------------------------------------------------------------------------- 1 | {% import "components/home.html" as home with context %} 2 | {% extends "layout/basic.html" %} 3 | {% block content %} 4 |
5 |
6 | {% block home_content %}{% endblock %} 7 |
8 |
9 | {{ home.render_sidebar() }} 10 | {% block home_sidebar %}{% endblock %} 11 |
12 |
13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /packages/ui-default/templates/layout/immersive.html: -------------------------------------------------------------------------------- 1 | {% set layout_name = "immersive" %} 2 | {% set no_path_section = true %} 3 | {% extends "layout/html5.html" %} 4 | {% block body %} 5 | 6 | {% include "partials/nav.html" %} 7 | 8 |
9 |
10 | {% include "partials/header_mobile.html" %} 11 | 12 |
13 | {% block content %}{% endblock %} 14 |
15 | 16 | {% set show_topics = false %} 17 | {% include "partials/footer.html" %} 18 |
19 | 20 | {% include "partials/login_dialog.html" %} 21 | 22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /packages/ui-default/templates/layout/simple.html: -------------------------------------------------------------------------------- 1 | {% set layout_name = "basic" %} 2 | {% extends "layout/html5.html" %} 3 | {% block body %} 4 | {% endblock %} 5 | -------------------------------------------------------------------------------- /packages/ui-default/templates/layout/wiki_base.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/basic.html" %} 2 | {% block content %} 3 |
4 |
5 | {% block wiki_content %}{% endblock %} 6 |
7 |
8 |
9 | 13 |
14 |
15 |
16 | {% endblock %} -------------------------------------------------------------------------------- /packages/ui-default/templates/main.html: -------------------------------------------------------------------------------- 1 | {% set page_name = "homepage" %} 2 | {% extends "layout/basic.html" %} 3 | {% import "components/contest.html" as contest with context %} 4 | {% import "components/problem.html" as problem with context %} 5 | {% import "components/user.html" as user with context %} 6 | {% block content %} 7 |
8 | {% for column in contents %} 9 |
10 | {% for s in column.sections %} 11 | {% if templateExists("partials/homepage/" + s[0] + ".html") %} 12 | {% set payload = s[1] %} 13 | {% include "partials/homepage/" + s[0] + ".html" %} 14 | {% else %} 15 | {% set payload = _("Template {0} not found.").format(s[0]) %} 16 | {% include "partials/homepage/error.html" %} 17 | {% endif %} 18 | {% endfor %} 19 |
20 | {% endfor %} 21 |
22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /packages/ui-default/templates/manage_base.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/basic.html" %} 2 | {% block content %} 3 |
4 |
5 | {% block manage_content %}{% endblock %} 6 |
7 |
8 |
9 | 21 |
22 |
23 |
24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /packages/ui-default/templates/manage_config.html: -------------------------------------------------------------------------------- 1 | {% extends "manage_base.html" %} 2 | {% block manage_content %} 3 | {{ set(UiContext, 'schema', schema) }} 4 | {{ set(UiContext, 'config', value) }} 5 |
6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /packages/ui-default/templates/manage_setting.html: -------------------------------------------------------------------------------- 1 | {% extends "manage_base.html" %} 2 | {% block manage_content %} 3 |
4 | {% include "partials/setting.html" %} 5 |
6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /packages/ui-default/templates/manage_user_import.html: -------------------------------------------------------------------------------- 1 | {% import "components/user.html" as user %} 2 | {% extends "manage_base.html" %} 3 | {% block manage_content %} 4 |
5 |
6 |

{{ _('Import User') }}

7 |
8 |
9 | {{ noscript_note.render() }} 10 |

11 | {{ form.form_textarea({ 12 | columns:null, 13 | label:'Users', 14 | name:'users', 15 | nospellcheck: true 16 | }) }} 17 |
18 | 19 | 20 |
21 |
22 |
23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/category.html: -------------------------------------------------------------------------------- 1 |
    2 | {% set _categories = (handler.domain.problemCategories or model.system.get('problem.categories'))|parseYaml %} 3 | {%- for category, sub_categories in _categories -%} 4 |
  • 5 |

    6 | {{ category }} 7 |

    8 | {% if sub_categories | length > 0 %} 9 |
      10 | {%- for sub_category in sub_categories -%} 11 |
    1. 12 | {{ sub_category }} 13 |
    2. 14 | {%- endfor -%} 15 |
    16 | {% endif %} 17 |
  • 18 | {%- endfor -%} 19 |
-------------------------------------------------------------------------------- /packages/ui-default/templates/partials/contest.html: -------------------------------------------------------------------------------- 1 | 2 | {{ tdoc.title }} 3 | -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/contest_user.html: -------------------------------------------------------------------------------- 1 | {% import "components/user.html" as user with context %} 2 | {% import "components/contest.html" as contest with context %} 3 | 4 | {%- for tsdoc in tsdocs -%} 5 | 6 | {{ tsdoc.uid }} 7 | {{ user.render_inline(udict[tsdoc.uid], badge=false) }} 8 | {{ contest.render_time(tsdoc.startAt or tdoc.beginAt) }} 9 | {{ contest.render_time(tsdoc.endAt or tdoc.endAt) }} 10 | {{ _('UnRank') if tsdoc.unrank else _('Rank') }} 11 | 12 | {{ _('Rank') if tsdoc.unrank else _('UnRank') }} 13 | 14 | 15 | {%- endfor -%} 16 | -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/discussion_nodes_widget.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ _('Discussion Nodes') }}

4 |
5 |
    6 | {%- for category, nodes in vnodes|groupby('content') -%} 7 |
  • 8 |

    {{ category }}

    9 |
      10 | {%- for node in nodes -%} 11 |
    1. {{ node.docId }}
    2. 12 | {%- endfor -%} 13 |
    14 |
  • 15 | {%- endfor -%} 16 |
17 |
-------------------------------------------------------------------------------- /packages/ui-default/templates/partials/hamburger.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/header_mobile.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 | 7 |
8 |
9 | {% include "partials/hamburger.html" %} 10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/homepage/bulletin.html: -------------------------------------------------------------------------------- 1 | {% if domain.bulletin %} 2 |
3 |
4 | {{ domain.bulletin|markdown|safe }} 5 |
6 |
7 | {% endif %} -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/homepage/discussion.html: -------------------------------------------------------------------------------- 1 | {% set ddocs = payload[0] %} 2 | {% set vndict = payload[1] %} 3 | {% if ddocs.length %} 4 |
5 |
6 |

{{ _('Discussion') }}

7 |
8 | {% include "partials/discussion_list.html" %} 9 | 12 |
13 | {% endif %} -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/homepage/discussion_nodes.html: -------------------------------------------------------------------------------- 1 | {% if handler.user.hasPerm(perm.PERM_VIEW_DISCUSSION) %} 2 | {% set vnodes = payload %} 3 | {% include 'partials/discussion_nodes_widget.html' %} 4 | {% endif %} -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/homepage/error.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{ payload }} 4 |
5 |
-------------------------------------------------------------------------------- /packages/ui-default/templates/partials/homepage/hitokoto.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ _('Hitokoto') }}

4 |
5 |
6 |
7 | -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/homepage/problem_search.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ _('Search') }}

4 |
5 |
6 |
7 | 10 | 11 |
12 |
13 |
-------------------------------------------------------------------------------- /packages/ui-default/templates/partials/homepage/recent_problems.html: -------------------------------------------------------------------------------- 1 | {% set pdocs = payload[0] %} 2 | {% set psdict = payload[1] %} 3 | {% if pdocs.length %} 4 |
5 |
6 |

{{ _('Recent Problems') }}

7 |
8 |
9 | {%- for pdoc in pdocs -%} 10 |

11 | {{ problem.render_problem_title(pdoc, show_tags=false, show_invisible_flag=false, show_pid=false) }} 12 | {{ datetimeSpan(pdoc._id)|safe }} 13 |

14 | {%- endfor -%} 15 |
16 |
17 | {% endif %} -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/homepage/starred_problems.html: -------------------------------------------------------------------------------- 1 | {% set pdocs = payload[0] %} 2 | {% if pdocs.length %} 3 |
4 |
5 |

{{ _('Starred Problems') }}

6 |
7 |
8 | {%- for pdoc in pdocs -%} 9 |

10 | {{ problem.render_problem_title(pdoc, show_tags=false, show_invisible_flag=false) }} 11 |

12 | {%- endfor -%} 13 |
14 |
15 | {% endif %} -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/homework.html: -------------------------------------------------------------------------------- 1 | 2 | {{ tdoc.title }} 3 | -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/homework_default_penalty_rules.yaml: -------------------------------------------------------------------------------- 1 | # Format: 2 | # hours: coefficient 3 | 1: 0.9 4 | 3: 0.8 5 | 12: 0.75 6 | 9999: 0.5 7 | -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/problem.html: -------------------------------------------------------------------------------- 1 | {% import "components/problem.html" as problem with context %} 2 | {{ problem.render_problem_title(pdoc, inline=true) }} -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/problem_default.md: -------------------------------------------------------------------------------- 1 | # Background 2 | Special for beginners, ^_^ 3 | 4 | # Description 5 | Given two integers x and y, print the sum. 6 | 7 | # Format 8 | 9 | ## Input 10 | Two integers x and y, satisfying $0\leq x,y\leq 32767$ . 11 | 12 | ## Output 13 | One integer, the sum of x and y. 14 | 15 | # Samples 16 | 17 | ```input1 18 | 123 500 19 | ``` 20 | 21 | ```output1 22 | 623 23 | ``` 24 | 25 | # Limitation 26 | 1s, 1024KiB for each test case. 27 | -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/problem_lucky.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 | {{ _('Lucky') }} 5 | 6 |

7 |
8 |

{{ _('Pick a problem randomly based on the current filter.') }}

9 |
10 |
11 | -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/problem_sidebar.html: -------------------------------------------------------------------------------- 1 | {% if not owner_udoc %} 2 | {% set owner_udoc = udoc %} 3 | {% endif %} 4 | {% if tdoc %} 5 | {% if tdoc.rule == 'homework' %} 6 | {% include "partials/problem_sidebar_homework.html" %} 7 | {% include "partials/homework_sidebar.html" %} 8 | {% else %} 9 | {% include "partials/problem_sidebar_contest.html" %} 10 | {% include "partials/contest_sidebar.html" %} 11 | {% endif %} 12 | {% else %} 13 | {% include "partials/problem_sidebar_normal.html" %} 14 | {% endif %} 15 | -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/problem_stat.html: -------------------------------------------------------------------------------- 1 |
2 |

{{ _('{0} problems' if pcountRelation == 'eq' else 'Over {0} problems').format(pcount) }}

3 |
-------------------------------------------------------------------------------- /packages/ui-default/templates/partials/training_default.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "_id": 1, 4 | "title": "最初的最初 - A+B Problem", 5 | "requireNids": [], 6 | "pids": ["P1000"] 7 | }, 8 | { 9 | "_id": 2, 10 | "title": "最初的进阶", 11 | "requireNids": [1], 12 | "pids": [2, 3] 13 | } 14 | ] 15 | -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/user.html: -------------------------------------------------------------------------------- 1 | {% import "components/user.html" as user with context %} 2 | {{ user.render_inline(udoc, badge=false) }} -------------------------------------------------------------------------------- /packages/ui-default/templates/partials/user_detail/activity.html: -------------------------------------------------------------------------------- 1 |
  • 2 |

    {{ _('Recent Activities') }}

    3 |
    4 | {% if not tdocs.length %} 5 | {{ nothing.render("This person is lazy and didn't join any contests or homework.") }} 6 | {% else %} 7 |
    8 | 15 |
    16 | {% endif %} 17 |
    18 |
  • -------------------------------------------------------------------------------- /packages/ui-default/templates/user_changemail_mail.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/mail.html" %} 2 | {% block title %}{{ _('Change Email') }}{% endblock %} 3 | {% block content %} 4 |

    {{ _('Hello, {0}! You can click following link to active your new email of your {1} account:').format(uname, handler.domain.ui.name|default(model.system.get('server.name'))) }}

    5 |

    {{ url_prefix }}{{ path }}

    6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /packages/ui-default/templates/user_changemail_mail_sent.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/immersive.html" %} 2 | {% block content %} 3 |
    4 |

    {{ _('New Email') }}

    5 |
    6 | {{ _('Confirmation mail has been sent to your new email.') }} 7 |
    8 |
    9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /packages/ui-default/templates/user_delete_pending.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/immersive.html" %} 2 | {% block content %} 3 |
    4 |

    {{ _('Done') }}

    5 |
    6 | {{ _('Your account will be deleted in 7 days.') }} 7 | {{ _('You can cancel this at user setting page.') }} 8 |
    9 |
    10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /packages/ui-default/templates/user_logout.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/immersive.html" %} 2 | {% block content %} 3 |
    4 |
    5 |

    {{ _('Logout') }}

    6 |
    7 |
    8 | 9 |
    10 |
    11 |
    12 |
    13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /packages/ui-default/templates/user_lostpass_mail.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/mail.html" %} 2 | {% block title %}{{ _('Lost Password') }}{% endblock %} 3 | {% block content %} 4 |

    {{ _('Hello, {0}! You can click following link to reset the password of your {1} account:').format(uname, handler.domain.ui.name|default(model.system.get('server.name'))) }}

    5 |

    {{ url_prefix }}{{ url }}

    6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /packages/ui-default/templates/user_lostpass_mail_sent.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/immersive.html" %} 2 | {% block content %} 3 |
    4 |

    {{ _('Lost Password') }}

    5 |
    6 | {{ _('Password reset mail has been sent to your email.') }} 7 |
    8 |
    9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /packages/ui-default/templates/user_register.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/immersive.html" %} 2 | {% block content %} 3 |
    4 |
    5 |

    {{ _('Sign Up') }}

    6 |
    7 |
    8 | 12 |
    13 |
    14 |
    15 | {{ captcha|safe }} 16 | 17 |
    18 |
    19 |
    20 |
    21 |
    22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /packages/ui-default/templates/user_register_mail.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/mail.html" %} 2 | {% block title %}{{ _('Sign Up') }}{% endblock %} 3 | {% block content %} 4 |

    {{ _('Hello! You can click following link to sign up your {0} account:').format(handler.domain.ui.name|default(model.system.get('server.name'))) }}

    5 |

    {{ url_prefix|safe }}{{ path|safe }}

    6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /packages/ui-default/templates/user_register_mail_sent.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/immersive.html" %} 2 | {% block content %} 3 |
    4 |

    {{ _('Sign Up') }}

    5 |
    6 | {{ _('Sign up mail has been sent to your email.') }} 7 |
    8 |
    9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /packages/ui-default/templates/user_sudo_redirect.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/immersive.html" %} 2 | {% block content %} 3 |
    4 |
    5 |

    {{ _('Confirm Access') }}

    6 |
    7 | Redirecting, please wait... 8 |
    9 |
    10 | {% for k, v in args %} 11 | 12 | {% endfor %} 13 | 14 |
    15 | 16 |
    17 |
    18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /packages/ui-default/typed.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg?react' { 2 | import { ComponentType } from 'react'; 3 | const content: ComponentType; 4 | export default content; 5 | } 6 | declare module '*.vue' { 7 | export default {} as any; 8 | } 9 | declare module '*.css' { 10 | const content: string; 11 | export default content; 12 | } 13 | declare module '*.styl' { 14 | const content: string; 15 | export default content; 16 | } 17 | declare module '*.scss' { 18 | const content: string; 19 | export default content; 20 | } 21 | -------------------------------------------------------------------------------- /packages/ui-default/utils/mediaQuery.ts: -------------------------------------------------------------------------------- 1 | export function isAbove(width) { 2 | if (window.matchMedia) { 3 | return window.matchMedia(`(min-width: ${width}px)`).matches; 4 | } 5 | return window.innerWidth >= width; 6 | } 7 | 8 | export function isBelow(width) { 9 | if (window.matchMedia) { 10 | return window.matchMedia(`(max-width: ${width}px)`).matches; 11 | } 12 | return window.innerWidth <= width; 13 | } 14 | 15 | Object.assign(window.Hydro.utils, { isAbove, isBelow }); 16 | -------------------------------------------------------------------------------- /packages/ui-next/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Hydro 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/ui-next/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/ui-next", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "tsc -b && vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "@vitejs/plugin-react": "^4.5.1", 12 | "react": "^18.3.1", 13 | "react-dom": "^18.3.1" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.3.23", 17 | "@types/react-dom": "^18.3.7", 18 | "@vitejs/plugin-react-swc": "^3.10.1", 19 | "vite": "^6.3.5" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/ui-next/src/App.tsx: -------------------------------------------------------------------------------- 1 | function App() { 2 | return
    ; 3 | } 4 | 5 | export default App; 6 | -------------------------------------------------------------------------------- /packages/ui-next/src/main.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import App from './App'; 4 | 5 | createRoot(document.getElementById('root')!).render( 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /packages/ui-next/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/utils/lib/locate-pm2.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | const PATH = process.env.PATH?.split(':') || []; 5 | 6 | /* eslint-disable import/no-dynamic-require */ 7 | // @ts-ignore 8 | const pm2: typeof import('pm2') | null = (() => { 9 | for (const dir of PATH) { 10 | try { 11 | const info = fs.readlinkSync(path.resolve(dir, 'pm2')); 12 | const p = path.resolve(dir, info); 13 | if (p.startsWith('/nix')) return require(`${p.split('/bin')[0]}/lib/node_modules/pm2`); 14 | // installed by yarn 15 | return require(`${p.split('.bin')[0]}pm2`); 16 | } catch (e) { } 17 | } 18 | return null; 19 | })(); 20 | 21 | export default pm2; 22 | -------------------------------------------------------------------------------- /packages/utils/lib/register.js: -------------------------------------------------------------------------------- 1 | console.warn('@hydroooj/utils/lib/register has moved to @hydrooj/register.'); 2 | require('@hydrooj/register'); 3 | -------------------------------------------------------------------------------- /packages/utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/utils", 3 | "version": "1.4.34-beta.3", 4 | "description": "hydrooj utils", 5 | "main": "lib/utils.ts", 6 | "repository": "https://github.com/hydro-dev/Hydro.git", 7 | "author": "undefined ", 8 | "license": "AGPL-3.0-or-later", 9 | "preferUnplugged": true, 10 | "optionalPeerDependencies": { 11 | "bson": "^6.17.0", 12 | "moment-timezone": "^0.5.48" 13 | }, 14 | "dependencies": { 15 | "@hydrooj/register": "workspace:^", 16 | "cac": "^6.7.14", 17 | "fs-extra": "^11.3.0", 18 | "js-yaml": "^4.1.0", 19 | "reggol": "^2.1.0", 20 | "search-query-parser": "^1.6.0", 21 | "systeminformation": "^5.27.1" 22 | }, 23 | "devDependencies": { 24 | "@types/fs-extra": "^11.0.4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/vjudge/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hydrooj/vjudge", 3 | "version": "1.10.0-beta.1", 4 | "description": "Submit problems to remote oj", 5 | "main": "./src/index.ts", 6 | "repository": "https://github.com/hydro-dev/Hydro.git", 7 | "author": "undefined", 8 | "license": "AGPL-3.0-or-later", 9 | "preferUnplugged": true, 10 | "dependencies": { 11 | "@hydrooj/utils": "workspace:^", 12 | "jsdom": "^26.1.0", 13 | "superagent-charset": "^1.2.0", 14 | "superagent-proxy": "^3.0.0" 15 | }, 16 | "devDependencies": { 17 | "@types/jsdom": "^21.1.7", 18 | "@types/superagent-proxy": "^3.0.4" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/vjudge/src/providers/index.ts: -------------------------------------------------------------------------------- 1 | import codeforces from './codeforces'; 2 | import csgoj from './csgoj'; 3 | import hduoj from './hduoj'; 4 | import { 5 | HUSTOJ as hustoj, 6 | XJOI as xjoi, 7 | } from './hustoj'; 8 | import poj from './poj'; 9 | import spoj from './spoj'; 10 | import uoj from './uoj'; 11 | import yacs from './yacs'; 12 | 13 | const vjudge: Record = { 14 | codeforces, 15 | csgoj, 16 | poj, 17 | spoj, 18 | uoj, 19 | hustoj, 20 | xjoi, 21 | hduoj, 22 | yacs, 23 | }; 24 | export default vjudge; 25 | -------------------------------------------------------------------------------- /packages/vjudge/src/proxy.ts: -------------------------------------------------------------------------------- 1 | declare module 'superagent' { 2 | interface Request { 3 | proxy(url: string): this; 4 | } 5 | } 6 | export default {}; 7 | -------------------------------------------------------------------------------- /test/entry.js: -------------------------------------------------------------------------------- 1 | process.env.CI = true; 2 | const version = process.versions.node.split('.').map((i) => i.padStart(2, '0')); 3 | version.pop(); 4 | if (+version.join('.') < 18.08) throw new Error('Tests only available in NodeJS>=18.8'); 5 | require('hydrooj/bin/hydrooj'); 6 | require('./main'); 7 | --------------------------------------------------------------------------------