diff --git a/src/App.vue b/src/App.vue
index 684578f..110fc8b 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -23,13 +23,17 @@
data() {
return {
interval: profile.typingSpeed,
+ charsPerTick: profile.charsPerTick,
fullStyle: profile.desktopStyles,
fullMarkdown: profile.resumeMarkdown,
thanksMarkdown: profile.thanksMarkdown,
currentStyle: '',
currentMarkdown: '',
currentThankMarkdown: '',
- enableHtml: false
+ enableHtml: false,
+ htmlShown: false,
+ thanksShown: false,
+ styleFull: profile.desktopStyles.join('')
}
},
created() {
@@ -37,79 +41,101 @@
},
methods: {
makeResume: async function () {
- // Type every stage of the stylesheet in order. The original code called
- // progressivelyShowStyle(1) three times between steps, but that call is
- // idempotent once the cumulative-length guard is satisfied, so stages 2
- // and 3 were never typed and the whole .thankEditor stylesheet (padding,
- // link colours, box layout) was missing from the injected
+
\ No newline at end of file
diff --git a/src/Mobile.vue b/src/Mobile.vue
index fcfa04e..68c7132 100644
--- a/src/Mobile.vue
+++ b/src/Mobile.vue
@@ -2,6 +2,7 @@
+
@@ -9,19 +10,24 @@
import profile from './config/profile.js'
import StyleEditor from './components/StyleEditor.vue'
import ResumeEditor from './components/ResumeEditor.vue'
+ import ThankEditor from './components/ThankEditor.vue'
import './assets/reset.css'
export default {
name: 'app',
- components: { StyleEditor, ResumeEditor },
+ components: { StyleEditor, ResumeEditor, ThankEditor },
data() {
return {
interval: profile.typingSpeed,
+ charsPerTick: profile.charsPerTick,
fullStyle: profile.mobileStyles,
fullMarkdown: profile.resumeMarkdown,
+ thanksMarkdown: profile.thanksMarkdown,
currentStyle: '',
currentMarkdown: '',
- enableHtml: false
+ currentThankMarkdown: '',
+ enableHtml: false,
+ styleFull: profile.mobileStyles.join('')
}
},
created() {
@@ -29,58 +35,88 @@
},
methods: {
makeResume: async function () {
- await this.progressivelyShowStyle(0)
- await this.progressivelyShowResume()
- await this.progressivelyShowStyle(1)
+ await this.typeStyleUpToMarker('/* Start writing resume */')
await this.showHtml()
- await this.progressivelyShowStyle(1)
+ await this.typeStyleRemaining()
+ await this.progressivelyShowThanks()
+ },
+ typeStyleUpToMarker(marker) {
+ return new Promise((resolve) => {
+ const markerIdx = this.styleFull.indexOf(marker)
+ const target = markerIdx >= 0 ? markerIdx + marker.length : this.styleFull.length
+ this.progressivelyShowStylePartial(target, resolve)
+ })
+ },
+ typeStyleRemaining() {
+ return new Promise((resolve) => {
+ this.progressivelyShowStyleAll(resolve)
+ })
},
showHtml() {
return new Promise((resolve) => {
this.enableHtml = true
- this.$nextTick(() => this.$refs.resumeEditor.goTop())
- resolve()
- })
- },
- progressivelyShowStyle(n) {
- return new Promise((resolve) => {
- const interval = this.interval
- const showStyle = (() => {
- const style = this.fullStyle[n]
- if (!style) return
- const length = this.fullStyle.filter((_, index) => index <= n).map((item) => item.length).reduce((p, c) => p + c, 0)
- const prefixLength = length - style.length
- if (this.currentStyle.length < length) {
- const l = this.currentStyle.length - prefixLength
- this.currentStyle += style.substring(l, l + 1) || ' '
- if (style.substring(l - 1, l) === '\n' && this.$refs.styleEditor) {
- this.$nextTick(() => this.$refs.styleEditor.goBottom())
- }
- setTimeout(showStyle, interval)
- } else {
- resolve()
- }
+ this.$nextTick(() => {
+ if (this.$refs.resumeEditor) this.$refs.resumeEditor.goTop()
+ resolve()
})
- showStyle()
})
},
- progressivelyShowResume() {
+ progressivelyShowStyleAll(resolve) {
+ const total = this.styleFull.length
+ const step = this.increment(total)
+ const tick = () => {
+ if (this.currentStyle.length < total) {
+ step()
+ setTimeout(tick, this.interval)
+ } else {
+ resolve()
+ }
+ }
+ tick()
+ },
+ progressivelyShowStylePartial(target, resolve) {
+ const step = this.increment(target)
+ const tick = () => {
+ if (this.currentStyle.length < target) {
+ step()
+ setTimeout(tick, this.interval)
+ } else {
+ resolve()
+ }
+ }
+ tick()
+ },
+ increment(total) {
+ const cpt = this.charsPerTick
+ return () => {
+ const cur = this.currentStyle.length
+ if (cur >= total) return
+ const end = Math.min(total, cur + cpt)
+ const slice = this.styleFull.substring(cur, end)
+ this.currentStyle += slice
+ if (slice.includes('\n') && this.$refs.styleEditor) {
+ this.$nextTick(() => this.$refs.styleEditor.goBottom())
+ }
+ }
+ },
+ progressivelyShowThanks() {
return new Promise((resolve) => {
- const length = this.fullMarkdown.length
+ const length = this.thanksMarkdown.length
const interval = this.interval
- const showResume = () => {
- if (this.currentMarkdown.length < length) {
- this.currentMarkdown = this.fullMarkdown.substring(0, this.currentMarkdown.length + 1)
- const prevChar = this.currentMarkdown[this.currentMarkdown.length - 2]
- if (prevChar === '\n' && this.$refs.resumeEditor) {
- this.$nextTick(() => this.$refs.resumeEditor.goBottom())
- }
- setTimeout(showResume, interval)
+ const cpt = this.charsPerTick
+ const showThanks = () => {
+ if (this.currentThankMarkdown.length < length) {
+ this.currentThankMarkdown = this.thanksMarkdown.substring(0, Math.min(length, this.currentThankMarkdown.length + cpt))
+ this.$nextTick(() => {
+ // Keep the newly revealed thanks section in view as it grows.
+ window.scrollTo(0, document.body.scrollHeight)
+ })
+ setTimeout(showThanks, interval)
} else {
resolve()
}
}
- showResume()
+ showThanks()
})
}
}
@@ -101,4 +137,4 @@
*{
box-sizing: border-box;
}
-
+
\ No newline at end of file
diff --git a/src/config/profile.js b/src/config/profile.js
index cc3a44c..1511d29 100644
--- a/src/config/profile.js
+++ b/src/config/profile.js
@@ -2,11 +2,14 @@ export default {
// Personal info
name: 'Mr. Hoelee',
title: 'Full Stack Engineer · AI & Automation · DevOps',
- githubUrl: 'https://git.hoelee.com/hoelee',
+ githubUrl: 'https://github.com/hoelee/hoelee-profile',
+ giteaUrl: 'https://git.hoelee.com/hoelee',
- // Animation speed (ms per character typed)
- // Lower = faster. Recommended range: 1 (instant) to 20 (dramatic slow)
+ // Animation speed — per-tick delay (ms) and characters written per tick.
+ // Effective speed = typingSpeed / charsPerTick. Lower delay = faster;
+ // higher charsPerTick = fewer setTimeout hops = much faster and smoother.
typingSpeed: 1,
+ charsPerTick: 5,
// Resume content in Markdown
resumeMarkdown: `Mr. Hoelee (Lee Teong Hoe)
@@ -90,7 +93,8 @@ The best fit is a team that needs one person who can own the whole stack: backen
* WhatsApp → [+6012-7972969](https://wa.me/60127972969)
* Email → [me@hoelee.com](mailto:me@hoelee.com)
-* GitHub → [git.hoelee.com/hoelee](https://git.hoelee.com/hoelee)
+* Gitea → [git.hoelee.com/hoelee](https://git.hoelee.com/hoelee)
+* GitHub → [github.com/hoelee](https://github.com/hoelee/)
* Based in Perak, Malaysia — remote-friendly, open to hybrid in Malaysia
Where I can contribute:
@@ -105,6 +109,7 @@ Where I can contribute:
desktopStyles: [
`/*
* Gitea: https://git.hoelee.com/hoelee
+ * GitHub: https://github.com/hoelee
* Hi, I'm Mr. Hoelee — Full Stack Engineer, AI & Automation, DevOps
* Let me create an animated resume!
*/
@@ -120,11 +125,11 @@ html {
#content{
height:70vh;
margin:0;
+ overflow: hidden;
}
#foot{
- height:30vh;
+ min-height:30vh;
margin:0;
- overflow: hidden;
}
/* Set border */
@@ -208,14 +213,14 @@ html{
* Thanks for everyone's attention.
*/
.styleEditor{
- width: calc(49vw - 1em);
+ width: calc(50vw - 1em);
height:70vh;
- margin-left:1vw;
+ margin-left:.5em;
}
.resumeEditor{
- width: calc(49vw - 1em);
+ width: calc(50vw - 1em);
height:70vh;
- margin-right:1vw;
+ margin-right:.5em;
}
.thankEditor{
@@ -250,9 +255,10 @@ html{
}
.thankEditor {
- width: 99vw; height: calc(30vh - 1.5em);
+ width: 99vw; min-height: calc(30vh - 1.5em);
border: 1px solid #ccc;
font-size: .9em;
+ overflow: visible;
}
`,`
.thankEditor{
@@ -276,18 +282,19 @@ html{
}
.thankEditor{
- width: 95vw;
- height: calc(30vh - 1.5em);
+ width: 95vw;
+ min-height: calc(30vh - 1.5em);
margin: .5em auto;
padding: 1.25em 1.75em;
box-sizing: border-box;
+ overflow: visible;
-webkit-transition: none;
transition: none;
-webkit-transform: rotateX(-10deg) translateZ(-200px);
transform: rotateX(-10deg) translateZ(-200px);
}
-/* keep the page itself from ever scrolling */
+/* keep horizontal scrolling off; allow vertical scroll when short */
body{
overflow-x: hidden;
}
@@ -297,6 +304,7 @@ body{
mobileStyles: [
`/*
* Gitea: https://git.hoelee.com/hoelee
+ * GitHub: https://github.com/hoelee
* Hi, I'm Mr. Hoelee — Full Stack Engineer, AI & Automation, DevOps
* Let me create an animated resume!
*/
@@ -315,9 +323,10 @@ html {
padding: .5em;
border: 1px solid;
overflow: auto;
- width: 96vw;
+ width: 94vw;
margin: 2.3vh 2vw;
height: 90vh;
+ box-sizing: border-box;
}
/* Too short */
.styleEditor {
@@ -342,19 +351,20 @@ html{
perspective: 1000px;
}
.styleEditor {
- position: fixed; left: 0; top: 0;
+ position: absolute; left: 0; top: 0;
transform: rotateX(-10deg) translateZ(-50px) ;
}
/* Prepare an editor */
.resumeEditor{
- position: fixed;
- top: 48%; left: 0;
+ position: relative;
+ top: 47vh; left: 0;
padding: .5em; margin: 1.5vh;
width: 94vw; height: 48.5vh;
border: 1px solid;
background: white; color: #222;
overflow: auto;
+ box-sizing: border-box;
}
/* Start writing resume */
@@ -414,10 +424,44 @@ html{
.resumeEditor a:hover {
text-decoration: underline;
}
-/* keep the page itself from ever scrolling */
-html, body{
+
+/* Thank-you panel below the resume, full width */
+.thankEditor {
+ position: relative;
+ width: 94vw;
+ margin: 2vh auto;
+ padding: 1.25em 1.25em;
+ box-sizing: border-box;
+ background: #E9D9BB;
+ color: #001C42;
+ overflow: auto;
+ border: 1px solid #ccc;
+ font-size: .9em;
+}
+.thankEditor h2 {
+ margin-top: 0;
+ padding-bottom: .35em;
+ border-bottom: 2px solid rgba(0,28,66,.35);
+}
+.thankEditor a {
+ color: #0b4a6f;
+ font-weight: 600;
+}
+.thankEditor ul, .thankEditor ol {
+ list-style: none;
+ padding-left: .25em;
+}
+.thankEditor ul > li::before {
+ content: '☞';
+ color: #0b4a6f;
+ margin-right: .5em;
+}
+
+/* allow vertical scroll, never horizontal */
+html, body {
overflow-x: hidden;
+ overflow-y: auto;
max-width: 100%;
}
`]
-}
+}
\ No newline at end of file