Compare commits

..
Author SHA1 Message Date
Annie Huang 5069ac12da fix map view 2024-04-11 23:39:57 +08:00
Lin, Hung-Shin cec4d86058 build(deps): bump tar from 6.2.0 to 6.2.1 (#131)
Bumps [tar](https://github.com/isaacs/node-tar) from 6.2.0 to 6.2.1.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/isaacs/node-tar/commit/bef7b1e4ffab822681fea2a9b22187192ed14717"><code>bef7b1e</code></a>
6.2.1</li>
<li><a
href="https://github.com/isaacs/node-tar/commit/fe8cd57da5686f8695415414bda49206a545f7f7"><code>fe8cd57</code></a>
prevent extraction in excessively deep subfolders</li>
<li><a
href="https://github.com/isaacs/node-tar/commit/fe7ebfdcede1f8a2e65db12e19ecc4b3a9934648"><code>fe7ebfd</code></a>
remove security.md</li>
<li>See full diff in <a
href="https://github.com/isaacs/node-tar/compare/v6.2.0...v6.2.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tar&package-manager=npm_and_yarn&previous-version=6.2.0&new-version=6.2.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/linux-odyssey/linux-odyssey/network/alerts).

</details>
2024-04-11 22:39:51 +08:00
dependabot[bot] 93b0bae204 build(deps): bump tar from 6.2.0 to 6.2.1
Bumps [tar](https://github.com/isaacs/node-tar) from 6.2.0 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.2.0...v6.2.1)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-11 11:56:58 +00:00
Lin, Hung-Shin 4713b11c95 Fix e2e Testing (#130)
**Fix e2e broken caused by website modification**

- Add back "GameTitle" for **Linux Odyssey** in header
- Delete echo hint in quest **move** (It stucks the testing)
- Modify paths for several testing describes
- Fix type problems in command.ts
2024-04-11 15:19:38 +08:00
Annie Huang 36e6bc10d3 fix: e2e broken caused by website modification 2024-04-09 11:32:58 +08:00
Annie Huang 580d5fc3f8 Feat landing page - Mobile Optimisation (#112)
This optimisation is for mobile usage and also make header a better
component.
- Header Restruction
- Menu pop up when using smaller screen
- Seperate header components and other components
2024-04-07 23:49:53 +08:00
Annie Huang d8392ec3f6 delete redundant code 2024-04-07 23:44:41 +08:00
Annie Huang 5f99f42f5f fix type problems 2024-04-07 20:41:17 +08:00
Annie Huang bdf757c2d4 fix: header components in the middle 2024-04-07 20:40:44 +08:00
Annie Huang 453bfb7f1e delete redundant code 2024-04-06 22:06:48 +08:00
wancat 3b470ed952 fix: session store type 2024-04-06 22:06:48 +08:00
Annie Huang b4af2d46d3 fix hint display problems 2024-04-06 22:06:48 +08:00
Annie Huang 0e96e30b96 categorise components 2024-04-06 22:06:48 +08:00
Annie Huang 3e3ac58c6b Restruct heading 2024-04-06 22:06:48 +08:00
wancat 9f690548cf fix: session store type 2024-04-06 22:06:48 +08:00
Annie Huang cd2ec6df75 fix hint display problems 2024-04-06 22:06:48 +08:00
Annie Huang 8e7b5f2552 modify menu position while open 2024-04-06 22:06:48 +08:00
Annie Huang 385c6bc110 delete testing id 2024-04-06 22:06:48 +08:00
wancat 092cf284dd fix: session store type 2024-04-06 22:06:48 +08:00
Annie Huang 7806852cab fix hint display problems 2024-04-06 22:06:48 +08:00
Annie Huang 1e31cf3be5 fix file locations and transfer to ts 2024-04-06 22:06:29 +08:00
Annie Huang aa903fbacf fix file position 2024-04-06 22:06:29 +08:00
Annie Huang df980a7d56 delete redundant code 2024-04-06 22:06:29 +08:00
Annie Huang 980d0497f4 fix header justify end property 2024-04-06 22:06:29 +08:00
Annie Huang e37a23c34f mobile menu (but not justify end while bigger screen, still solving) 2024-04-06 22:06:29 +08:00
Annie Huang f4fc6de4cc canceling [..vh] 2024-04-06 22:06:29 +08:00
Annie Huang 9bfc6614ca hide some text when screen is small 2024-04-06 22:06:29 +08:00
Annie Huang 8c4a9ea871 add header to login and register page, modified header class but not fit for all circumstances 2024-04-06 22:06:29 +08:00
Annie Huang ad29137b86 categorise components - change path 2024-04-06 22:06:29 +08:00
Annie Huang b5e49547ea categorise components 2024-04-06 22:06:29 +08:00
Annie Huang 9a4f9b958a Restruct heading 2024-04-06 22:06:29 +08:00
Annie Huang 29602785cd rwd player opinions 2024-04-06 22:05:59 +08:00
29 changed files with 337 additions and 477 deletions

No files matched your search

+1 -1
View File
@@ -177,7 +177,7 @@ onMounted(async () => {
class="p-2 stroke-2 scale-y-120 justify-center items-center"
/>
<h1
class="p-4 absolute w-fit z-2 font-mono flex flax-wrap text-xl"
class="p-10 absolute w-fit z-2 font-mono flex flax-wrap text-xl"
style="width: 30%; height: 6%; font-size: 3vh; color: #00ff00"
>
踏上你的Linux冒險之旅吧!
@@ -2,6 +2,6 @@
<font-awesome-icon :icon="['fas', 'list']" class="text-text-primary p-1" />
<h1 id="CommandBook" class="inline text-text-primary font-xl">指令之書</h1>
<div id="cheatsheets">
<p class="text-text-secondary">--指令列表--</p>
<p class="text-text-secondary">-- --指令列表-- --</p>
</div>
</template>
@@ -4,7 +4,7 @@ import { useToast } from 'vue-toastification'
import { ref } from 'vue'
import { Vue3Lottie } from 'vue3-lottie'
// import { surveyUrl } from '../config'
import CompleteAnimation from '../img/complete-animation.json'
import CompleteAnimation from '../../img/complete-animation.json'
const showModal = ref(true) // Controls whether the modal is visible or not
const router = useRouter()
@@ -26,7 +26,6 @@ const backtoMap = async () => {
try {
router.push({ name: 'map' })
} catch (err) {
// useToast().error('Failed to load map')
toast.error('無法讀取地圖')
console.error(err)
}
@@ -60,17 +59,6 @@ setTimeout(() => {
<p class="text-base text-text text-center">
接下來回到地圖,迎接新的挑戰吧!
</p>
<!-- <p class="text-base text-text">
您已成功完成挑戰,真的非常感謝您的參與!<br />
如果您能撥冗填寫這份問卷,我們將不勝感激,這對我們非常重要!
</p> -->
<!-- <a
id="SurveyButton"
:href="surveyUrl"
target="_blank"
class="inline-flex justify-center rounded-lg text-base font-black py-2 mt-3 bg-text-primary w-full"
>填寫問卷</a
> -->
<a
id="BacktoMap"
target="_blank"
@@ -1,8 +1,8 @@
<script setup lang="ts">
import { useRouter } from 'vue-router'
import { useToast } from 'vue-toastification'
import { createSession } from '../store/session'
import { surveyUrl } from '../config'
import { createSession } from '../../store/session'
import { surveyUrl } from '../../config'
const router = useRouter()
const reset = async () => {
@@ -20,29 +20,31 @@ const continuePlay = async () => {
</script>
<template>
<section class="h-[10%] p-3 grid grid-cols-4 gap-4 place-content-between">
<section
class="h-fit w-fit px-1 py-1 grid grid-cols-4 gap-2 place-content-between"
>
<button id="survey" class="bg-bg-secondary rounded-lg p-2">
<a title="Survey" :href="surveyUrl" target="_blank" class="h-5 w-5">
<font-awesome-icon
:icon="['fas', 'file-invoice']"
class="text-text-primary"
class="text-text-primary px-1"
/>
<p class="text-text-primary inline ml-1">問卷</p>
<p class="text-text-primary inline ml-1 hidden md:flex">問卷</p>
</a>
</button>
<button disabled id="solution" class="bg-bg-disabled rounded-lg p-2">
<font-awesome-icon
:icon="['far', 'circle-question']"
class="text-text-disabled"
class="text-text-disabled px-1"
/>
<p class="text-text-disabled inline ml-1">解答</p>
<p class="text-text-disabled inline ml-1 hidden md:flex">解答</p>
</button>
<button id="reset" class="bg-bg-secondary rounded-lg p-2" @click="reset">
<font-awesome-icon
:icon="['fas', 'arrow-rotate-left']"
class="text-text-primary"
class="text-text-primary px-1"
/>
<p class="text-text-primary inline ml-1">重來</p>
<p class="text-text-primary inline ml-1 hidden md:flex">重來</p>
</button>
<button
id="continue"
@@ -51,9 +53,9 @@ const continuePlay = async () => {
>
<font-awesome-icon
:icon="['far', 'circle-right']"
class="text-text-primary"
class="text-text-primary px-1"
/>
<p class="text-text-primary inline ml-1">繼續</p>
<p class="text-text-primary inline ml-1 hidden md:flex">繼續</p>
</button>
</section>
</template>
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref, watch } from 'vue'
import sessionStore from '../store/session'
import MarkdownText from './MarkdownText.vue'
import sessionStore from '../../store/session'
import MarkdownText from '../MarkdownText.vue'
const current = ref(-1)
watch(
@@ -27,16 +27,12 @@ const right = () => {
<template>
<div class="h-full flex flex-col">
<!-- Hint Header -->
<div class="flex items-center p-2 h-10">
<font-awesome-icon
:icon="['far', 'lightbulb']"
class="text-yellow-200 p-2 content-center"
/>
<h1 class="inline text-text w-1/2 font-xl p-2 m-1">
<!-- Hint -->
提示
</h1>
<h1 class="inline text-text w-1/2 font-xl p-2 m-1">提示</h1>
<div v-if="current !== -1" class="flex w-full justify-end items-end">
<button
class="p-2 m-1 w-1/8"
@@ -1,8 +1,8 @@
<script setup lang="ts">
import { computed } from 'vue'
import MarkdownText from './MarkdownText.vue'
import sessionStore from '../store/session'
import { Task } from '../types'
import MarkdownText from '../MarkdownText.vue'
import sessionStore from '../../store/session'
import { Task } from '../../types'
const tasks = computed(() => {
return sessionStore.session.tasks.map((task: Task) => {
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useTerminal } from '../store/session'
import { useTerminal } from '../../store/session'
const terminal = ref<HTMLElement | null>(null)
@@ -1,6 +1,6 @@
<script setup lang="ts">
import FileNode from './FileNode.vue'
import sessionStore from '../store/session'
import FileNode from '../FileNode.vue'
import sessionStore from '../../store/session'
</script>
<template>
@@ -0,0 +1,108 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useToast } from 'vue-toastification'
import { logout } from '../../utils/auth'
import { bugReportUrl, surveyUrl } from '../../config'
import { reset } from '../../store/session'
import userProfileStore, {
loadUserProfile,
resetUserProfile,
} from '../../store/userProfile'
const router = useRouter()
const toast = useToast()
const handleLogout = async () => {
try {
await logout()
resetUserProfile()
reset()
router.push({ name: 'login' })
} catch (err) {
toast.error('登出失敗')
console.error(err)
}
}
onMounted(async () => {
try {
await loadUserProfile()
} catch (err) {
toast.error('無法讀取使用者資料')
console.error(err)
}
})
</script>
<template>
<!-- <button id="warning" class="flex items-center h-5 w-5">
<font-awesome-icon
:icon="['fas', 'triangle-exclamation']"
class="text-text-primary h-full w-full"
/>
</button>
<button id="setting" class="flex items-center h-5 w-5">
<font-awesome-icon
:icon="['fas', 'gear']"
class="text-text-primary h-full w-full"
/>
</button>
<button id="fullscreen" class="flex items-center h-5 w-5">
<font-awesome-icon
:icon="['fas', 'expand']"
class="text-text-primary h-full w-full"
/>
</button> -->
<p
id="UsernameText"
class="text-text inline-block whitespace-nowrap px-1.5"
style="font-size: 2vh"
>
{{ userProfileStore.username }}
</p>
<a
title="Survey"
:href="surveyUrl"
target="_blank"
class="md:h-5 md:w-5 px-1.5 w-auto"
>
<font-awesome-icon
:icon="['fas', 'file-invoice']"
class="text-text-primary inline"
/>
<span class="text-text sm:hidden px-2">Survey</span>
</a>
<RouterLink
title="LeaderBoard"
to="/leaderboard"
class="md:h-5 md:w-5 px-1.5 w-auto"
>
<font-awesome-icon :icon="['fas', 'trophy']" class="text-text-primary" />
<span class="text-text sm:hidden px-2">Leader Board</span>
</RouterLink>
<RouterLink title="Map" to="/map" class="md:h-5 md:w-5 px-1.5 w-auto">
<font-awesome-icon :icon="['fas', 'map']" class="text-text-primary" />
<span class="text-text sm:hidden px-2">Map</span>
</RouterLink>
<a
title="Bug Report"
:href="bugReportUrl"
target="_blank"
class="md:h-5 md:w-5 px-1.5 w-auto"
>
<font-awesome-icon :icon="['fas', 'bug']" class="text-text-primary" />
<span class="text-text sm:hidden px-2">Bug Report</span>
</a>
<button
title="Sign Out"
@click="handleLogout"
class="md:h-5 md:w-5 px-1.5 w-auto"
>
<font-awesome-icon
:icon="['fas', 'arrow-right-from-bracket']"
class="text-text-primary"
/>
<span class="text-text sm:hidden px-2">Sign Out</span>
</button>
</template>
+66
View File
@@ -0,0 +1,66 @@
<script setup lang="ts">
import { defineProps, ref } from 'vue'
defineProps({
title: {
type: String,
required: false,
},
headerComponent: {
type: Object,
required: false,
},
})
const menuOpen = ref(false)
const windowWidth = window.innerWidth < 768
const toggleMenu = () => {
menuOpen.value = !menuOpen.value
}
</script>
<template>
<div
class="w-full top-0 px-5 fixed flex flex-row flex-wrap bg-bg-secondary justify-between items-center sm:justify-between"
>
<div class="flex flex-row items-center gap-3" :class="{ hidden: menuOpen }">
<img src="../../img/icon_totem.svg" class="h-8 justify-self-start" />
<RouterLink
to="/"
id="GameTitle"
class="text-text-primary inline-block font-bold whitespace-nowrap pt-1 transition ease-in-out hover:scale-110 duration-300"
style="font-size: 2vh"
>
Linux Odyssey
</RouterLink>
<p
id="QuestTitle"
class="text-text inline-block font-bold whitespace-nowrap"
style="font-size: 2vh"
>
{{ title }}
</p>
</div>
<div
class="w-full flex flex-col items-center justify-end sm:w-auto sm:block sm:ml-6"
:class="{ hidden: !menuOpen }"
>
<component :is="headerComponent" />
</div>
<div
v-if="windowWidth"
:class="{ 'w-full flex justify-end pb-3': menuOpen }"
>
<button
type="button"
class="flex items-center h-5 w-5 rounded-md sm:hidden"
aria-controls="mobile-menu"
aria-expanded="false"
@click="toggleMenu"
>
<font-awesome-icon
:icon="['fas', 'bars']"
class="text-text-primary h-full w-full"
/>
</button>
</div>
</div>
</template>
@@ -0,0 +1,62 @@
<script setup lang="ts">
import { bugReportUrl } from '../../config'
const scrollToSection = (sectionId: string) => {
const section = document.getElementById(sectionId)
if (section) {
const container = document.querySelector('.overflow-y-auto') as HTMLElement // Replace '.overflow-y-auto' with the appropriate class or selector for your container
if (container) {
container.scrollTo({
top: section.offsetTop - container.offsetTop,
behavior: 'smooth', // Add smooth scrolling behavior if desired
})
}
}
}
</script>
<template>
<button
id="Intro"
class="text-text inline-block font-bold whitespace-nowrap pt-1 px-2 hover:text-text-primary"
@click="scrollToSection('intro')"
style="font-size: 2vh"
>
前言
</button>
<button
id="Feature"
class="text-text inline-block font-bold whitespace-nowrap pt-1 px-2 hover:text-text-primary"
@click="scrollToSection('feature')"
style="font-size: 2vh"
>
特色</button
><button
id="Info"
class="text-text inline-block font-bold whitespace-nowrap pt-1 px-2 hover:text-text-primary"
@click="scrollToSection('about')"
style="font-size: 2vh"
>
關於
</button>
<button
id="Contact"
class="text-text inline-block font-bold whitespace-nowrap pt-1 px-2 hover:text-text-primary"
@click="scrollToSection('contact')"
style="font-size: 2vh"
>
聯絡我們
</button>
<RouterLink
title="Sign In"
id="SignIn"
to="/register"
class="text-text inline-block font-bold whitespace-nowrap pt-1 px-2 hover:text-text-primary"
style="font-size: 2vh"
>
登入
</RouterLink>
<a title="Bug Report" :href="bugReportUrl" target="_blank" class="h-5 w-5">
<font-awesome-icon :icon="['fas', 'bug']" class="text-text-primary" />
</a>
</template>
+15 -11
View File
@@ -2,14 +2,15 @@
import { computed, onMounted } from 'vue'
import { useToast } from 'vue-toastification'
import sessionStore, { init } from '../store/session'
import GameHeaderPart from '../components/GameHeaderPart.vue'
import HeaderPart from '../components/header/HeaderPart.vue'
import GameHeaderComponents from '../components/header/GameHeaderComponents.vue'
// import CommandlistPart from '../components/CommandlistPart.vue'
import HintPart from '../components/HintPart.vue'
import QuestPart from '../components/QuestPart.vue'
import TerminalPart from '../components/TerminalPart.vue'
import VisualizationPart from '../components/VisualizationPart.vue'
import ControlPalette from '../components/ControlPalette.vue'
import CompleteModal from '../components/CompleteModal.vue'
import HintPart from '../components/game/HintPart.vue'
import QuestPart from '../components/game/QuestPart.vue'
import TerminalPart from '../components/game/TerminalPart.vue'
import VisualizationPart from '../components/game/VisualizationPart.vue'
import ControlPalette from '../components/game/ControlPalette.vue'
import CompleteModal from '../components/game/CompleteModal.vue'
import { LoadQuestError, LoadSessionError } from '../utils/errors'
const completed = computed(() => {
@@ -45,8 +46,11 @@ onMounted(async () => {
<template>
<!-- game header -->
<div class="h-[6vh] w-full">
<GameHeaderPart :title="sessionStore.quest?.title" />
<div class="w-full">
<HeaderPart
:title="sessionStore.quest?.title"
:headerComponent="GameHeaderComponents"
/>
</div>
<!-- current status indicator -->
<input
@@ -55,7 +59,7 @@ onMounted(async () => {
:value="sessionStore.session.status"
/>
<!-- main -->
<div id="main" class="h-[94vh] w-full flex p-3 space-x-3">
<div id="main" class="h-full pt-10 w-full flex p-3 space-x-3">
<!-- Topic and Command List -->
<div class="bg-bg-secondary h-full w-1/4 rounded-lg">
<section id="quest" class="h-full p-3 overflow-auto">
@@ -67,7 +71,7 @@ onMounted(async () => {
</section> -->
</div>
<!-- Terminal and Hint -->
<div class="bg-bg h-full w-1/2 rounded-lg">
<div class="bg-bg h-full rounded-lg w-1/2">
<section id="hint" class="h-3/5">
<HintPart />
</section>
+27 -101
View File
@@ -1,97 +1,19 @@
<script setup lang="ts">
import ContactButton from '../components/ContactButton.vue'
import DynamicBackground from '../components/DynamicBackground.vue'
import { bugReportUrl, facebookUrl, githubUrl } from '../config'
import LandingHeaderComponents from '../components/header/LandingHeaderComponents.vue'
import HeaderPart from '../components/header/HeaderPart.vue'
const scrollToSection = (sectionId: string) => {
const section = document.getElementById(sectionId)
if (section) {
const container = document.querySelector('.overflow-y-auto') // Replace '.overflow-y-auto' with the appropriate class or selector for your container
if (container) {
container.scrollTo({
top: section.offsetTop,
behavior: 'smooth', // Add smooth scrolling behavior if desired
})
}
}
}
import { facebookUrl, githubUrl } from '../config'
</script>
<template>
<div class="w-screen h-screen bg-scroll bg-bg">
<DynamicBackground class="w-full h-full" />
<div class="h-screen w-screen absolute top-0 left-0">
<!-- game header -->
<div
class="w-full h-[6vh] px-5 flex flex-row items-center justify-between bg-bg-secondary"
>
<div class="flex flex-row items-center gap-3">
<img src="../img/icon_totem.svg" class="h-8 justify-self-start" />
<button
id="GameTitle"
class="text-text-primary inline-block font-bold whitespace-nowrap pt-1"
style="font-size: 2vh"
>
Linux Odyssey
</button>
</div>
<div class="flex flex-row items-center gap-3">
<button
id="Intro"
class="text-text inline-block font-bold whitespace-nowrap pt-1"
@click="scrollToSection('intro')"
style="font-size: 2vh"
>
Intro
</button>
<button
id="Feature"
class="text-text inline-block font-bold whitespace-nowrap pt-1"
@click="scrollToSection('feature')"
style="font-size: 2vh"
>
Feature</button
><button
id="Info"
class="text-text inline-block font-bold whitespace-nowrap pt-1"
@click="scrollToSection('about')"
style="font-size: 2vh"
>
About
</button>
<button
id="Contact"
class="text-text inline-block font-bold whitespace-nowrap pt-1"
@click="scrollToSection('contact')"
style="font-size: 2vh"
>
Contact Us
</button>
<RouterLink
title="Sign In"
id="SignIn"
to="/register"
class="text-text inline-block font-bold whitespace-nowrap pt-1"
style="font-size: 2vh"
>
Sign In
</RouterLink>
<a
title="Bug Report"
:href="bugReportUrl"
target="_blank"
class="h-5 w-5"
>
<font-awesome-icon
:icon="['fas', 'bug']"
class="text-text-primary h-full w-full"
/>
</a>
</div>
</div>
<HeaderPart :headerComponent="LandingHeaderComponents" class="z-50" />
<!-- main content -->
<main class="w-full h-[94vh] overflow-y-auto">
<main class="w-full h-full overflow-y-auto">
<div class="w-full flex flex-col items-center">
<div
class="flex flex-col md:flex-row mt-10 justify-around gap-5 items-center my-10"
@@ -104,7 +26,6 @@ const scrollToSection = (sectionId: string) => {
Odyssey
</h1>
</div>
<!-- intro -->
<div class="w-full md:w-2/3 px-5">
<section
@@ -228,40 +149,43 @@ const scrollToSection = (sectionId: string) => {
鈦坦敏捷開發特別獎 第二名
</h1>
</div>
<!-- <h1
class="text-text-primary p-5 font-extrabold font-mono col-span-3 gap-3"
style="font-size: 3.5vh"
<div class="container mx-auto">
<div
class="justify-items-center grid grid-cols-1 sm:grid-cols-3 gap-5"
>
<h1
class="text-text-primary text-3xl font-extrabold font-mono sm:col-span-3 gap-6"
>
玩家感想
<!--可以做成翻頁卡牌一次一個-->
</h1>
<h1 class="text-text p-3" style="font-size: 2.5vh">
"很好玩很像益智遊戲"
</h1>
<h1 class="text-text p-3" style="font-size: 2.5vh">
<h1 class="text-xl text-center">"很好玩很像益智遊戲"</h1>
<h1 class="text-xl text-center">
"我從來不知道自由軟體的存在,謝謝你們。"
</h1>
<h1 class="text-text p-3" style="font-size: 2.5vh">
<h1 class="text-xl text-center">
"還蠻好玩的,讓程式變好玩"
</h1>
<h1 class="text-text p-3" style="font-size: 2.5vh">
<h1 class="text-xl text-center">
"以前不喜歡電腦課現在覺得喜歡一點點"
</h1>
<h1 class="text-text p-3" style="font-size: 2.5vh">
<h1 class="text-xl text-center">
"程式真的對新手很友善,讓我學到了很多東西"
</h1>
<h1 class="text-text p-3" style="font-size: 2.5vh">
<h1 class="text-xl text-center">
"感覺做得很好很簡明易懂關卡設計很用心"
</h1>
<h1 class="text-text p-3" style="font-size: 2.5vh">
<h1 class="text-xl text-center">
"這個體驗我覺得非常有趣,可以體驗到沒辦法體驗的程式,學長也都熱心指導,非常感謝學長的用心的講解,希望以後還可以再體驗這種活動"
</h1>
<h1 class="text-text p-3" style="font-size: 2.5vh">
<h1 class="text-xl text-center">
"以前我覺得寫程式很難不過現在覺得很有趣"
</h1>
<h1 class="text-text p-3" style="font-size: 2.5vh">
<h1 class="text-xl text-center">
"以前覺得寫程式是我生活中碰不到而且很難的項目。但通過這堂課之後,覺得寫程式不再那麼難以親近"
</h1> -->
</h1>
</div>
</div>
</section>
</div>
<section
@@ -271,7 +195,9 @@ const scrollToSection = (sectionId: string) => {
<h1 class="text-text-primary font-extrabold font-mono text-3xl">
聯絡我們
</h1>
<div class="flex flex-row items-center justify-around mt-5 gap-5">
<div
class="flex flex-row items-center justify-around mt-5 gap-5 mb-5"
>
<ContactButton
:url="facebookUrl"
:icons="['fab', 'facebook']"
+4 -3
View File
@@ -2,7 +2,8 @@
import { ref, onMounted } from 'vue'
import { useToast } from 'vue-toastification'
import DynamicBackground from '../components/DynamicBackground.vue'
import GameHeaderPart from '../components/GameHeaderPart.vue'
import HeaderPart from '../components/header/HeaderPart.vue'
import GameHeaderComponents from '../components/header/GameHeaderComponents.vue'
import api from '../utils/api'
const leaderboard = ref<
@@ -35,8 +36,8 @@ onMounted(async () => {
<div
class="w-screen h-screen absolute top-0 left-0 flex flex-wrap justify-center content-center"
>
<div class="h-[6vh] w-full">
<GameHeaderPart title="排行榜" />
<div class="w-full">
<HeaderPart title="排行榜" :headerComponent="GameHeaderComponents" />
</div>
<div class="w-fit h-[94vh] m-3">
<div
+4 -2
View File
@@ -1,6 +1,7 @@
<script setup lang="ts">
import AuthForm from '../components/AuthForm.vue'
import Background from '../components/DynamicBackground.vue'
import DynamicBackground from '../components/DynamicBackground.vue'
import HeaderPart from '../components/header/HeaderPart.vue'
import {
TooManyRequestsError,
@@ -45,10 +46,11 @@ const handleLogin = async ({
<template>
<div class="w-screen h-screen">
<Background class="w-full h-full" />
<DynamicBackground class="w-full h-full" />
<div
class="w-screen h-screen absolute top-0 left-0 flex flex-wrap justify-center content-center"
>
<HeaderPart />
<div class="w-fit">
<AuthForm @onSubmit="handleLogin" type="login" />
</div>
+9 -4
View File
@@ -1,13 +1,18 @@
<script setup lang="ts">
import QuestMap from '../components/QuestMap.vue'
import GameHeaderPart from '../components/GameHeaderPart.vue'
import HeaderPart from '../components/header/HeaderPart.vue'
import GameHeaderComponents from '../components/header/GameHeaderComponents.vue'
</script>
<template>
<div class="h-[6vh] w-full">
<GameHeaderPart title="關卡地圖" />
<div class="w-fit">
<HeaderPart
title="關卡地圖"
:headerComponent="GameHeaderComponents"
class="z-50"
/>
</div>
<div class="flex flex-col justify-center items-center w-full h-[94vh]">
<div class="flex flex-col justify-center items-center w-full h-full pt-5">
<QuestMap />
</div>
</template>
+5 -4
View File
@@ -3,8 +3,8 @@ import validator from 'validator'
import { isValidUsername } from '@linux-odyssey/utils'
import { passwordPolicy } from '@linux-odyssey/constants'
import AuthForm from '../components/AuthForm.vue'
import Background from '../components/DynamicBackground.vue'
import DynamicBackground from '../components/DynamicBackground.vue'
import HeaderPart from '../components/header/HeaderPart.vue'
import { checkUsername, register } from '../utils/auth'
import {
UnauthorizedError,
@@ -89,11 +89,12 @@ async function check({
<template>
<div class="w-screen h-screen">
<Background class="w-full h-full" />
<DynamicBackground class="w-full h-full" />
<div
class="h-screen w-screen absolute top-0 left-0 flex flex-wrap justify-center content-center"
>
<div class="w-fit">
<HeaderPart />
<div class="w-fit mt-8">
<AuthForm
@onSubmit="handleRegister"
@onChange="check"
-1
View File
@@ -8,5 +8,4 @@ mv world.txt wow.txt
ls
mv notes docs
ls
echo hint
mv docs/hello.txt hi.txt
-3
View File
@@ -1,3 +0,0 @@
FROM linuxodyssey/quest-base
COPY home ${HOME}
View File
Whitespace-only changes.
-278
View File
@@ -1,278 +0,0 @@
title: 查看檔案存取權限
instruction: |
在這個關卡中,你將會學到:
1. 用`ls -l`指令查看檔案的存取權限
2. 了解檔案存取權限的意義
準備好就輸入 `echo start` 開始吧!
requirements:
- helloworld
stages:
- id: start
task: 輸入 `echo start` 來開始
condition:
output:
- start
exceptions:
- condition:
command:
- ^echo$
responses:
- type: narrative
content:
- 你忘記輸入 `start` 了嗎?
- 請輸入 `echo start` 來開始
- catchAll: true
responses:
- type: narrative
content:
- 請輸入 `echo start` 來開始
responses:
- type: narrative
content: 現在我們要學習如何查看檔案的存取權限。
使用`ls -l`指令來查看不同檔案的存取權限吧!
hints:
- ➤ 使用 `ls -l` 來查看檔案的存取權限
- id: ls-l
task: 查看檔案的存取權限
requirements:
- start
condition:
command:
- ls -l
pwd:
- /home/commander
responses:
- type: narrative
content:
- 你可以看到檔案的存取權限的左邊類似 `-rw-r--r--` 的字串。
- 這個字串由十個字元組成,第一個字元代表檔案的類型,後面九個字元分別代表檔案的存取權限。
- 第一個字元如果是 `-` 代表這是一個檔案,如果是 `d` 代表這是一個資料夾。
- 後面九個字元中以三個字元一組,分別代表檔案的擁有者、群組、以及其他人的存取權限。
- 每一組中的三個字元分別代表讀取(`r`)、寫入(`w`)、執行(`x`)的權限。
- 如果該欄位是 `-` 則表示沒有該權限。
- 例如 `-w-` 代表擁有寫入的權限,但沒有讀取和執行的權限;
- 現在試著查看 `file1` 、 `file2` 、 `file3` 的內容吧!
- 如果沒有權限將會顯示 `Permission denied` 的訊息。
- id: read-file1
task: 嘗試查看 `file1` 的內容
requirements:
- ls-l
condition:
pwd: /home/commander
command:
- cat file1
- id: read-file2
task: 嘗試查看 `file2` 的內容
requirements:
- ls-l
condition:
pwd: /home/commander
command:
- cat file2
- id: read-file3
task: 嘗試查看 `file3` 的內容
requirements:
- ls-l
condition:
pwd: /home/commander
command:
- cat file3
- id: read-files
requirements:
- read-file1
- read-file2
- read-file3
responses:
- type: narrative
content:
- 你已經試著查看 `file1` 、 `file2` 、 `file3` 的內容了!
- 現在請用 `echo [檔案名稱]` 回答現在有哪的檔案的讀取權限。
# {unfinished}
- id: cat-continue
task: 使用 `echo continue` 來繼續
requirements:
- cat-hello
- cat-world
condition:
command:
- echo
responses:
- type: narrative
content:
- 接下來我們要來學習複製檔案。
- '`cp` 這個指令,是 copy 的縮寫,就是複製的意思。'
- 我們可以用 `cp [來源檔案] [目的檔案]` 的方式來複製檔案。
- 請你使用 `cp` 來複製 `hello.txt` 到 `hello2.txt`。
hints:
- ➤ 使用 `cp hello.txt hello2.txt` 來複製 `hello.txt` 到 `hello2.txt`
- id: cp-hello
task: 複製 `hello.txt` 到 `hello2.txt`
requirements:
- cat-continue
condition:
command:
- ^cp hello.txt hello2.txt$
files:
- path: hello2.txt
type: file
responses:
- type: narrative
content:
- 你成功複製 `hello.txt` 到 `hello2.txt` 了!
- 我們打開來確認一下 `hello2.txt` 的內容吧!
hints:
- ➤ 使用 `cat hello2.txt` 來查看 `hello2.txt` 的內容
- id: cat-hello2
requirements:
- cp-hello
task: 查看 `hello2.txt` 的內容
condition:
command:
- ^cat hello2.txt$
output:
- Hello
responses:
- type: narrative
content:
- 你成功查看到 `hello2.txt` 的內容了!
- 正如我們所見,`hello2.txt` 的內容跟 `hello.txt` 一模一樣。
- 那我們再來試試看複製 `world.txt` 到 `world2.txt` 吧!
hints:
- ➤ 使用 `cp world.txt world2.txt` 來複製 `world.txt` 到 `world2.txt`
- id: cp-world
requirements:
- cat-hello2
task: 複製 `world.txt` 到 `world2.txt`
condition:
command:
- ^cp world.txt world2.txt$
files:
- path: world2.txt
type: file
responses:
- type: narrative
content:
- 你成功複製 `world.txt` 到 `world2.txt` 了!
- 我們打開來確認一下 `world2.txt` 的內容吧!
hints:
- ➤ 使用 `cat world2.txt` 來查看 `world2.txt` 的內容
- id: cat-world2
requirements:
- cp-world
task: 查看 `world2.txt` 的內容
condition:
command:
- ^cat world2.txt$
output:
- World
responses:
- type: narrative
content:
- 你成功查看到 `world2.txt` 的內容了!
- '`world2.txt` 的內容跟 `world.txt` 一模一樣。'
- 接下來我們要來學習如何複製資料夾。
- 現在目錄下有一個 `notes` 資料夾,裡面有一個 `note.txt` 的檔案。
- 我們要能整個資料夾包含裡面的檔案一起複製到 `notes2`。
- 跟複製單一檔案不一樣的是,複製資料夾時,我們要加上 `-r` 的選項(recursive)代表將資料夾中的所有檔案都一起複製。
- 在使用指令時,如果想要調整指令的行為,可以在指令後面加上選項。
- 選項的格式通常是 `-` 加上一個字母,例如 `-r`。
- 選項擺放的位置沒有特別的限制,通常會跟在指令的後面,置於檔案名稱的前面。
- 請你使用 `cp -r notes notes2` 來複製 `notes` 資料夾到 `notes2`。
hints:
- ➤ 使用 `cp -r notes notes2` 來複製 `notes` 資料夾到 `notes2`
- ➤ 記得要加上 `-r` 的選項來複製所有檔案
- id: cp-notes
requirements:
- cat-world2
task: 複製 `notes` 資料夾到 `notes2`
condition:
command:
- ^cp
files:
- path: notes2
type: directory
- path: notes2/note.txt
type: file
responses:
- type: narrative
content:
- 你成功複製 `notes` 資料夾到 `notes2` 了!
- 我們打開來確認一下 `notes2` 資料夾的內容吧!
hints:
- ➤ 使用 `ls notes2` 來查看 `notes2` 資料夾的內容
exceptions:
- condition:
error:
- 'cp: omitting directory'
responses:
- type: narrative
content:
- 你忘記加上 `-r` 的參數了嗎?
- 請輸入 `cp -r notes notes2` 來複製 `notes` 資料夾到 `notes2`
- id: ls-notes2
requirements:
- cp-notes
task: 查看 `notes2` 資料夾的內容
condition:
command:
- ^ls notes2$
output:
- note.txt
responses:
- type: narrative
content:
- 你成功查看到 `notes2` 資料夾的內容了!
- 我們可以看到,整個 `notes` 資料夾包含裡面的檔案,都被複製到 `notes2` 資料夾了。
- 那麼最後我們要來試試看,如果複製一個檔案到已經存在的檔案會發生什麼事。
hints:
- ➤ 使用 `cp hello.txt world.txt` 來複製 `hello.txt` 到 `world.txt`
- id: cp-overwrite
requirements:
- ls-notes2
task: 複製 `hello.txt` 到 `world.txt`
condition:
command:
- ^cp hello.txt world.txt$
responses:
- type: narrative
content:
- 你成功複製 `hello.txt` 到 `world.txt` 了!
- 我們打開來確認一下 `world.txt` 的內容吧!
hints:
- ➤ 使用 `cat world.txt`
- id: END
requirements:
- cp-overwrite
task: 檢查 `world.txt` 的內容
condition:
command:
- ^cat world.txt$
output:
- Hello
responses:
- type: narrative
content:
- '`world.txt` 的內容變成 `Hello` 了!'
- 這是因為,當我們複製檔案時,如果目的檔案已經存在,那麼目的檔案的內容就會被覆蓋掉。
- 所以在複製檔案的時候,要特別注意目的檔案是否已經存在,否則可能會不小心把資料弄丟。
- 以上就是複製檔案的基本操作!
-1
View File
@@ -1 +0,0 @@
你有閱讀這個檔案的權限
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
echo
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
echo 你有執行這個檔案的權限
-12
View File
@@ -1,12 +0,0 @@
# 關卡名稱
## stage 名稱 {stage-id}
> {speaker=角色名稱}
> 對話內容
### exception1
content1
### exception2
content2
+4 -4
View File
@@ -5,7 +5,7 @@ export default function checkLoginUI() {
describe('example helloworld app', () => {
describe('Login tests', () => {
beforeEach(() => {
cy.visit('/')
cy.visit('/register')
})
it('Check Login Page Elements-Register', () => {
checkLoginUI()
@@ -83,11 +83,11 @@ describe('example helloworld app', () => {
cy.get('#UsernameText')
.should('contain', Cypress.env('defaultAccount'))
.and('be.visible')
cy.findByRole('link', { name: 'Survey' }).should('be.visible')
cy.findByRole('link', { name: 'LeaderBoard' }).should('be.visible')
cy.findByRole('link', { name: 'Map' }).should('be.visible')
cy.findByRole('link', { name: 'Bug Report' }).should('be.visible')
cy.findByRole('button', { name: 'Sign Out' }).should('be.visible')
cy.findByRole('button', { name: 'Map' }).should('be.visible')
cy.findByRole('button', { name: 'LeaderBoard' }).should('be.visible')
cy.findByRole('link', { name: 'Survey' }).should('be.visible')
})
it('Check QuestInfo', () => {
cy.get('#topic').should('contain', title)
+5 -5
View File
@@ -8,7 +8,7 @@ declare global {
namespace Cypress {
interface Chainable {
LoginWithPassword(username: string, password: string): Chainable<string>
PrepareForGame(): Chainable<void>
PrepareForGame(questId: string): Chainable<string>
InitTerminal(): Chainable<void>
typeInCommand(command: string): Chainable<string>
getQuestInfo(id: string): Chainable<JQuery<HTMLParagraphElement>>
@@ -38,8 +38,8 @@ Cypress.Commands.add('LoginWithPassword', (username, password) => {
cy.get('#password').type(password)
cy.get('#LogInOrSignUp').click()
})
Cypress.Commands.add('PrepareForGame', (questId) => {
cy.visit('/')
Cypress.Commands.add('PrepareForGame', (questId: string) => {
cy.visit('/login')
// make sure the login page is loaded
cy.url().should('satisfy', (elements: string) => {
const text = elements
@@ -143,9 +143,9 @@ Cypress.Commands.add('CompleteStageWithCommands', (stagename) => {
})
Cypress.Commands.add('CheckTextElement', (id, chText, enText) => {
if (Cypress.env('isCHVersion')) {
cy.get(id, { timeout: 50000 }).should('contain', chText).and('be.visible')
cy.get(id, { timeout: 7000 }).should('contain', chText).and('be.visible')
} else {
cy.get(id, { timeout: 50000 }).should('contain', enText).and('be.visible')
cy.get(id, { timeout: 7000 }).should('contain', enText).and('be.visible')
}
})
Cypress.Commands.add('CheckPlaceholder', (id, chText, enText) => {
+3 -3
View File
@@ -7717,9 +7717,9 @@ tar-stream@^2.0.0:
readable-stream "^3.1.1"
tar@^6.1.11:
version "6.2.0"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73"
integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==
version "6.2.1"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a"
integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"