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