Initial commit for Github
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"presets": [
|
||||
["es2015", { "modules": false }],
|
||||
"stage-3"
|
||||
],
|
||||
"plugins": ["transform-runtime"],
|
||||
"comments": false,
|
||||
"env": {
|
||||
"test": {
|
||||
"plugins": [ "istanbul" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
.pnp
|
||||
.pnp.js
|
||||
|
||||
# Production builds
|
||||
/dist
|
||||
/public/static/js
|
||||
/public/static/css
|
||||
|
||||
# Development
|
||||
.DS_Store
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Vite
|
||||
.vite/
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
Thumbs.db
|
||||
.DS_Store
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Package backup files (if you want to ignore them)
|
||||
package-backup.json
|
||||
package-modern.json
|
||||
package-updated-conservative.json
|
||||
package-vue2-working.json
|
||||
@@ -0,0 +1,117 @@
|
||||
# Mr. Hoelee Resume - Animated Resume
|
||||
|
||||
This project is inspired by: [Animating Resume](https://github.com/jirengu-inc/animating-resume)
|
||||
Deep respect to the original author.
|
||||
|
||||
An interactive animated resume built with Vue 3 and Vite that showcases your professional profile in a unique and engaging way. Features responsive design with separate mobile and desktop experiences.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Node.js**: >= 18.0.0 ([Download](https://nodejs.org/))
|
||||
- **npm**: >= 9.0.0
|
||||
- **Git**: [Download](https://git-scm.com/)
|
||||
|
||||
## Usage
|
||||
|
||||
1. Clone this repository:
|
||||
|
||||
```bash
|
||||
git clone <your-repository-url>
|
||||
cd hoelee-profile2
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
2. The development server will automatically open in your browser at `http://localhost:8080`
|
||||
|
||||
## Build and Deployment
|
||||
|
||||
### Building for Production
|
||||
|
||||
1. Build the project:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
2. The built files will be generated in the `dist/` directory
|
||||
|
||||
### Deployment Options
|
||||
|
||||
#### Option 1: Static Web Server
|
||||
|
||||
Upload the contents of the `dist/` directory to any static web server (Netlify, Vercel, GitHub Pages, etc.)
|
||||
|
||||
#### Option 2: Preview Locally
|
||||
|
||||
Test the production build locally:
|
||||
|
||||
```bash
|
||||
npm run preview
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
- `npm run dev` - Start development server with hot reload (opens at <http://localhost:8080>)
|
||||
- `npm run build` - Build for production (outputs to `dist/` directory)
|
||||
- `npm run preview` - Preview production build locally
|
||||
|
||||
## Project Structure
|
||||
|
||||
```text
|
||||
src/
|
||||
├── App.vue # Desktop version of the resume
|
||||
├── Mobile.vue # Mobile-optimized version
|
||||
├── main.js # Entry point with responsive logic
|
||||
├── assets/ # Stylesheets and static assets
|
||||
│ └── reset.css # CSS reset styles
|
||||
└── components/ # Resume components
|
||||
├── ResumeEditor.vue # Resume content editor
|
||||
├── StyleEditor.vue # Style/CSS editor
|
||||
└── ThankEditor.vue # Thank you section
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- **Responsive Design**: Automatically switches between desktop and mobile versions based on screen width (500px breakpoint)
|
||||
- **Interactive Animation**: Type-writer effect for resume content
|
||||
- **Live Preview**: Real-time preview of resume as it's being "typed"
|
||||
- **Modern Stack**: Built with Vue 3 composition API and Vite for fast development
|
||||
- **Syntax Highlighting**: Integrated Prism.js for code highlighting
|
||||
- **Markdown Support**: Uses marked.js for markdown parsing
|
||||
|
||||
## Customization
|
||||
|
||||
### Personalizing Your Resume
|
||||
|
||||
1. **Content**: Edit the resume content in `src/components/ResumeEditor.vue`
|
||||
2. **Styling**: Modify CSS styles in `src/components/StyleEditor.vue`
|
||||
3. **Layout**: Adjust the main layout in `src/App.vue` (desktop) or `src/Mobile.vue` (mobile)
|
||||
4. **Title**: Update the page title in `index.html`
|
||||
|
||||
### Configuration
|
||||
|
||||
- **Port**: Development server port is set to 8080 in `vite.config.js`
|
||||
- **Build Output**: Configured to output to `dist/` directory with organized asset structure
|
||||
- **Aliases**: Path aliases are set up for easy imports (`@`, `src`, `assets`, `components`)
|
||||
|
||||
## Technologies Used
|
||||
|
||||
- **Vue 3** - Progressive JavaScript framework with Composition API
|
||||
- **Vite** - Next-generation frontend build tool
|
||||
- **Prism.js** - Syntax highlighting library
|
||||
- **Marked** - Markdown parser and compiler
|
||||
- **CSS3 Animations** - Smooth typing animations and transitions
|
||||
- **PostCSS & Autoprefixer** - CSS processing and vendor prefixing
|
||||
|
||||
## Browser Support
|
||||
|
||||
This project uses modern JavaScript features and is compatible with:
|
||||
|
||||
- Chrome/Edge 88+
|
||||
- Firefox 85+
|
||||
- Safari 14+
|
||||
|
||||
## License
|
||||
|
||||
This project is for personal/educational use. Please respect the original author's work.
|
||||
@@ -0,0 +1 @@
|
||||
theme: jekyll-theme-cayman
|
||||
@@ -0,0 +1,40 @@
|
||||
// https://github.com/shelljs/shelljs
|
||||
require('./check-versions')()
|
||||
|
||||
process.env.NODE_ENV = 'production'
|
||||
|
||||
var ora = require('ora')
|
||||
var path = require('path')
|
||||
var chalk = require('chalk')
|
||||
var shell = require('shelljs')
|
||||
var webpack = require('webpack')
|
||||
var config = require('../config')
|
||||
var webpackConfig = require('./webpack.prod.conf')
|
||||
|
||||
var spinner = ora('building for production...')
|
||||
spinner.start()
|
||||
|
||||
var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory)
|
||||
shell.rm('-rf', assetsPath)
|
||||
shell.mkdir('-p', assetsPath)
|
||||
shell.config.silent = true
|
||||
shell.cp('-R', 'static/*', assetsPath)
|
||||
shell.config.silent = false
|
||||
|
||||
webpack(webpackConfig, function (err, stats) {
|
||||
spinner.stop()
|
||||
if (err) throw err
|
||||
process.stdout.write(stats.toString({
|
||||
colors: true,
|
||||
modules: false,
|
||||
children: false,
|
||||
chunks: false,
|
||||
chunkModules: false
|
||||
}) + '\n\n')
|
||||
|
||||
console.log(chalk.cyan(' Build complete.\n'))
|
||||
console.log(chalk.yellow(
|
||||
' Tip: built files are meant to be served over an HTTP server.\n' +
|
||||
' Opening index.html over file:// won\'t work.\n'
|
||||
))
|
||||
})
|
||||
@@ -0,0 +1,45 @@
|
||||
var chalk = require('chalk')
|
||||
var semver = require('semver')
|
||||
var packageConfig = require('../package.json')
|
||||
|
||||
function exec (cmd) {
|
||||
return require('child_process').execSync(cmd).toString().trim()
|
||||
}
|
||||
|
||||
var versionRequirements = [
|
||||
{
|
||||
name: 'node',
|
||||
currentVersion: semver.clean(process.version),
|
||||
versionRequirement: packageConfig.engines.node
|
||||
},
|
||||
{
|
||||
name: 'npm',
|
||||
currentVersion: exec('npm --version'),
|
||||
versionRequirement: packageConfig.engines.npm
|
||||
}
|
||||
]
|
||||
|
||||
module.exports = function () {
|
||||
var warnings = []
|
||||
for (var i = 0; i < versionRequirements.length; i++) {
|
||||
var mod = versionRequirements[i]
|
||||
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
|
||||
warnings.push(mod.name + ': ' +
|
||||
chalk.red(mod.currentVersion) + ' should be ' +
|
||||
chalk.green(mod.versionRequirement)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (warnings.length) {
|
||||
console.log('')
|
||||
console.log(chalk.yellow('To use this template, you must update following to modules:'))
|
||||
console.log()
|
||||
for (var i = 0; i < warnings.length; i++) {
|
||||
var warning = warnings[i]
|
||||
console.log(' ' + warning)
|
||||
}
|
||||
console.log()
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/* eslint-disable */
|
||||
require('eventsource-polyfill')
|
||||
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
|
||||
|
||||
hotClient.subscribe(function (event) {
|
||||
if (event.action === 'reload') {
|
||||
window.location.reload()
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,81 @@
|
||||
require('./check-versions')()
|
||||
|
||||
var config = require('../config')
|
||||
if (!process.env.NODE_ENV) {
|
||||
process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
|
||||
}
|
||||
|
||||
var opn = require('opn')
|
||||
var path = require('path')
|
||||
var express = require('express')
|
||||
var webpack = require('webpack')
|
||||
var proxyMiddleware = require('http-proxy-middleware')
|
||||
var webpackConfig = require('./webpack.dev.conf')
|
||||
|
||||
// default port where dev server listens for incoming traffic
|
||||
var port = process.env.PORT || config.dev.port
|
||||
// automatically open browser, if not set will be false
|
||||
var autoOpenBrowser = !!config.dev.autoOpenBrowser
|
||||
// Define HTTP proxies to your custom API backend
|
||||
// https://github.com/chimurai/http-proxy-middleware
|
||||
var proxyTable = config.dev.proxyTable
|
||||
|
||||
var app = express()
|
||||
var compiler = webpack(webpackConfig)
|
||||
|
||||
var devMiddleware = require('webpack-dev-middleware')(compiler, {
|
||||
publicPath: webpackConfig.output.publicPath,
|
||||
quiet: true
|
||||
})
|
||||
|
||||
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
|
||||
log: () => {}
|
||||
})
|
||||
// force page reload when html-webpack-plugin template changes
|
||||
compiler.plugin('compilation', function (compilation) {
|
||||
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
|
||||
hotMiddleware.publish({ action: 'reload' })
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
// proxy api requests
|
||||
Object.keys(proxyTable).forEach(function (context) {
|
||||
var options = proxyTable[context]
|
||||
if (typeof options === 'string') {
|
||||
options = { target: options }
|
||||
}
|
||||
app.use(proxyMiddleware(options.filter || context, options))
|
||||
})
|
||||
|
||||
// handle fallback for HTML5 history API
|
||||
app.use(require('connect-history-api-fallback')())
|
||||
|
||||
// serve webpack bundle output
|
||||
app.use(devMiddleware)
|
||||
|
||||
// enable hot-reload and state-preserving
|
||||
// compilation error display
|
||||
app.use(hotMiddleware)
|
||||
|
||||
// serve pure static assets
|
||||
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
|
||||
app.use(staticPath, express.static('./static'))
|
||||
|
||||
var uri = 'http://localhost:' + port
|
||||
|
||||
devMiddleware.waitUntilValid(function () {
|
||||
console.log('> Listening at ' + uri + '\n')
|
||||
})
|
||||
|
||||
module.exports = app.listen(port, function (err) {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
return
|
||||
}
|
||||
|
||||
// when env is testing, don't need open it
|
||||
if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
|
||||
opn(uri)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,64 @@
|
||||
var path = require('path')
|
||||
var config = require('../config')
|
||||
var ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
|
||||
exports.assetsPath = function (_path) {
|
||||
var assetsSubDirectory = process.env.NODE_ENV === 'production'
|
||||
? config.build.assetsSubDirectory
|
||||
: config.dev.assetsSubDirectory
|
||||
return path.posix.join(assetsSubDirectory, _path)
|
||||
}
|
||||
|
||||
exports.cssLoaders = function (options) {
|
||||
options = options || {}
|
||||
// generate loader string to be used with extract text plugin
|
||||
function generateLoaders (loaders) {
|
||||
var sourceLoader = loaders.map(function (loader) {
|
||||
var extraParamChar
|
||||
if (/\?/.test(loader)) {
|
||||
loader = loader.replace(/\?/, '-loader?')
|
||||
extraParamChar = '&'
|
||||
} else {
|
||||
loader = loader + '-loader'
|
||||
extraParamChar = '?'
|
||||
}
|
||||
return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '')
|
||||
}).join('!')
|
||||
|
||||
// Extract CSS when that option is specified
|
||||
// (which is the case during production build)
|
||||
if (options.extract) {
|
||||
return ExtractTextPlugin.extract({
|
||||
use: sourceLoader,
|
||||
fallback: 'vue-style-loader'
|
||||
})
|
||||
} else {
|
||||
return ['vue-style-loader', sourceLoader].join('!')
|
||||
}
|
||||
}
|
||||
|
||||
// http://vuejs.github.io/vue-loader/en/configurations/extract-css.html
|
||||
return {
|
||||
css: generateLoaders(['css']),
|
||||
postcss: generateLoaders(['css']),
|
||||
less: generateLoaders(['css', 'less']),
|
||||
sass: generateLoaders(['css', 'sass?indentedSyntax']),
|
||||
scss: generateLoaders(['css', 'sass']),
|
||||
stylus: generateLoaders(['css', 'stylus']),
|
||||
styl: generateLoaders(['css', 'stylus'])
|
||||
}
|
||||
}
|
||||
|
||||
// Generate loaders for standalone style files (outside of .vue)
|
||||
exports.styleLoaders = function (options) {
|
||||
var output = []
|
||||
var loaders = exports.cssLoaders(options)
|
||||
for (var extension in loaders) {
|
||||
var loader = loaders[extension]
|
||||
output.push({
|
||||
test: new RegExp('\\.' + extension + '$'),
|
||||
loader: loader
|
||||
})
|
||||
}
|
||||
return output
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
var utils = require('./utils')
|
||||
var config = require('../config')
|
||||
var isProduction = process.env.NODE_ENV === 'production'
|
||||
|
||||
module.exports = {
|
||||
loaders: utils.cssLoaders({
|
||||
sourceMap: isProduction
|
||||
? config.build.productionSourceMap
|
||||
: config.dev.cssSourceMap,
|
||||
extract: isProduction
|
||||
}),
|
||||
postcss: [
|
||||
require('autoprefixer')({
|
||||
browsers: ['last 2 versions']
|
||||
})
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
var path = require('path')
|
||||
var utils = require('./utils')
|
||||
var config = require('../config')
|
||||
var vueLoaderConfig = require('./vue-loader.conf')
|
||||
|
||||
function resolve (dir) {
|
||||
return path.join(__dirname, '..', dir)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
app: './src/main.js'
|
||||
},
|
||||
output: {
|
||||
path: config.build.assetsRoot,
|
||||
filename: '[name].js',
|
||||
publicPath: process.env.NODE_ENV === 'production'
|
||||
? config.build.assetsPublicPath
|
||||
: config.dev.assetsPublicPath
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue', '.json'],
|
||||
modules: [
|
||||
resolve('src'),
|
||||
resolve('node_modules')
|
||||
],
|
||||
alias: {
|
||||
'src': resolve('src'),
|
||||
'assets': resolve('src/assets'),
|
||||
'components': resolve('src/components')
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
options: vueLoaderConfig
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
include: [resolve('src'), resolve('test')]
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
query: {
|
||||
limit: 10000,
|
||||
name: utils.assetsPath('img/[name].[hash:7].[ext]')
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
query: {
|
||||
limit: 10000,
|
||||
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
var utils = require('./utils')
|
||||
var webpack = require('webpack')
|
||||
var config = require('../config')
|
||||
var merge = require('webpack-merge')
|
||||
var baseWebpackConfig = require('./webpack.base.conf')
|
||||
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
|
||||
|
||||
// add hot-reload related code to entry chunks
|
||||
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
|
||||
baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
|
||||
})
|
||||
|
||||
module.exports = merge(baseWebpackConfig, {
|
||||
module: {
|
||||
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
|
||||
},
|
||||
// cheap-module-eval-source-map is faster for development
|
||||
devtool: '#cheap-module-eval-source-map',
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': config.dev.env
|
||||
}),
|
||||
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
// https://github.com/ampedandwired/html-webpack-plugin
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: 'index.html',
|
||||
inject: true
|
||||
}),
|
||||
new FriendlyErrorsPlugin()
|
||||
]
|
||||
})
|
||||
@@ -0,0 +1,102 @@
|
||||
var path = require('path')
|
||||
var utils = require('./utils')
|
||||
var webpack = require('webpack')
|
||||
var config = require('../config')
|
||||
var merge = require('webpack-merge')
|
||||
var baseWebpackConfig = require('./webpack.base.conf')
|
||||
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
var ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
var env = config.build.env
|
||||
|
||||
var webpackConfig = merge(baseWebpackConfig, {
|
||||
module: {
|
||||
rules: utils.styleLoaders({
|
||||
sourceMap: config.build.productionSourceMap,
|
||||
extract: true
|
||||
})
|
||||
},
|
||||
devtool: config.build.productionSourceMap ? '#source-map' : false,
|
||||
output: {
|
||||
path: config.build.assetsRoot,
|
||||
filename: utils.assetsPath('js/[name].[chunkhash].js'),
|
||||
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
|
||||
},
|
||||
plugins: [
|
||||
// http://vuejs.github.io/vue-loader/en/workflow/production.html
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': env
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: false
|
||||
},
|
||||
sourceMap: true
|
||||
}),
|
||||
// extract css into its own file
|
||||
new ExtractTextPlugin({
|
||||
filename: utils.assetsPath('css/[name].[contenthash].css')
|
||||
}),
|
||||
// generate dist index.html with correct asset hash for caching.
|
||||
// you can customize output by editing /index.html
|
||||
// see https://github.com/ampedandwired/html-webpack-plugin
|
||||
new HtmlWebpackPlugin({
|
||||
filename: config.build.index,
|
||||
template: 'index.html',
|
||||
inject: true,
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeAttributeQuotes: true
|
||||
// more options:
|
||||
// https://github.com/kangax/html-minifier#options-quick-reference
|
||||
},
|
||||
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
|
||||
chunksSortMode: 'dependency'
|
||||
}),
|
||||
// split vendor js into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'vendor',
|
||||
minChunks: function (module, count) {
|
||||
// any required modules inside node_modules are extracted to vendor
|
||||
return (
|
||||
module.resource &&
|
||||
/\.js$/.test(module.resource) &&
|
||||
module.resource.indexOf(
|
||||
path.join(__dirname, '../node_modules')
|
||||
) === 0
|
||||
)
|
||||
}
|
||||
}),
|
||||
// extract webpack runtime and module manifest to its own file in order to
|
||||
// prevent vendor hash from being updated whenever app bundle is updated
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'manifest',
|
||||
chunks: ['vendor']
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
if (config.build.productionGzip) {
|
||||
var CompressionWebpackPlugin = require('compression-webpack-plugin')
|
||||
|
||||
webpackConfig.plugins.push(
|
||||
new CompressionWebpackPlugin({
|
||||
asset: '[path].gz[query]',
|
||||
algorithm: 'gzip',
|
||||
test: new RegExp(
|
||||
'\\.(' +
|
||||
config.build.productionGzipExtensions.join('|') +
|
||||
')$'
|
||||
),
|
||||
threshold: 10240,
|
||||
minRatio: 0.8
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (config.build.bundleAnalyzerReport) {
|
||||
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
|
||||
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
|
||||
}
|
||||
|
||||
module.exports = webpackConfig
|
||||
@@ -0,0 +1,6 @@
|
||||
var merge = require('webpack-merge')
|
||||
var prodEnv = require('./prod.env')
|
||||
|
||||
module.exports = merge(prodEnv, {
|
||||
NODE_ENV: '"development"'
|
||||
})
|
||||
@@ -0,0 +1,38 @@
|
||||
// see http://vuejs-templates.github.io/webpack for documentation.
|
||||
var path = require('path')
|
||||
|
||||
module.exports = {
|
||||
build: {
|
||||
env: require('./prod.env'),
|
||||
index: path.resolve(__dirname, '../public/index.html'),
|
||||
assetsRoot: path.resolve(__dirname, '../public'),
|
||||
assetsSubDirectory: 'static',
|
||||
assetsPublicPath: '/anires/public/',
|
||||
productionSourceMap: true,
|
||||
// Gzip off by default as many popular static hosts such as
|
||||
// Surge or Netlify already gzip all static assets for you.
|
||||
// Before setting to `true`, make sure to:
|
||||
// npm install --save-dev compression-webpack-plugin
|
||||
productionGzip: false,
|
||||
productionGzipExtensions: ['js', 'css'],
|
||||
// Run the build command with an extra argument to
|
||||
// View the bundle analyzer report after build finishes:
|
||||
// `npm run build --report`
|
||||
// Set to `true` or `false` to always turn it on or off
|
||||
bundleAnalyzerReport: process.env.npm_config_report
|
||||
},
|
||||
dev: {
|
||||
env: require('./dev.env'),
|
||||
port: 8080,
|
||||
autoOpenBrowser: true,
|
||||
assetsSubDirectory: 'static',
|
||||
assetsPublicPath: '/',
|
||||
proxyTable: {},
|
||||
// CSS Sourcemaps off by default because relative paths are "buggy"
|
||||
// with this option, according to the CSS-Loader README
|
||||
// (https://github.com/webpack/css-loader#sourcemaps)
|
||||
// In our experience, they generally work as expected,
|
||||
// just be aware of this issue when enabling this option.
|
||||
cssSourceMap: false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
NODE_ENV: '"production"'
|
||||
}
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<title>Mr Hoelee Resume</title>
|
||||
<link rel="icon" type="image/x-icon" href="./favicon.ico">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="./favicon.ico">
|
||||
<script type="module" crossorigin src="/static/js/index-BF7mfwu9.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/static/css/index-D2xCV1gI.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<title>Mr Hoelee Resume</title>
|
||||
<link rel="icon" type="image/x-icon" href="./favicon.ico">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="./favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Generated
+1502
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "animation-resume",
|
||||
"version": "2.0.0",
|
||||
"description": "animation resume - modernized",
|
||||
"author": " <>",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"marked": "^12.0.2",
|
||||
"prismjs": "^1.30.0",
|
||||
"vue": "^3.5.22"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.1.4",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"postcss": "^8.4.49",
|
||||
"vite": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.0.0",
|
||||
"npm": ">= 9.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "animation-resume",
|
||||
"version": "1.0.0",
|
||||
"description": "animation resume",
|
||||
"author": " <>",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "node build/dev-server.js",
|
||||
"build": "node build/build.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"marked": "^2.1.3",
|
||||
"prismjs": "^1.30.0",
|
||||
"vue": "^2.7.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^6.7.7",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-loader": "^6.4.1",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-stage-2": "^6.24.1",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"babel-register": "^6.26.0",
|
||||
"chalk": "^1.1.3",
|
||||
"connect-history-api-fallback": "^1.6.0",
|
||||
"css-loader": "^0.28.11",
|
||||
"eventsource-polyfill": "^0.9.6",
|
||||
"express": "^4.21.2",
|
||||
"extract-text-webpack-plugin": "^2.1.2",
|
||||
"file-loader": "^0.11.2",
|
||||
"friendly-errors-webpack-plugin": "^1.7.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
"http-proxy-middleware": "^0.17.4",
|
||||
"opn": "^4.0.2",
|
||||
"ora": "^1.4.0",
|
||||
"semver": "^5.7.2",
|
||||
"shelljs": "^0.7.8",
|
||||
"url-loader": "^0.6.2",
|
||||
"vue-loader": "^10.3.0",
|
||||
"vue-style-loader": "^2.0.5",
|
||||
"vue-template-compiler": "^2.7.16",
|
||||
"webpack": "^2.7.0",
|
||||
"webpack-bundle-analyzer": "^2.13.1",
|
||||
"webpack-dev-middleware": "^1.12.2",
|
||||
"webpack-hot-middleware": "^2.26.1",
|
||||
"webpack-merge": "^2.6.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0",
|
||||
"npm": ">= 6.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "hoelee-profile",
|
||||
"version": "2.0.0",
|
||||
"description": "Hoelee's animated resume - modernized",
|
||||
"author": "Hoelee <>",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"marked": "^12.0.2",
|
||||
"prismjs": "^1.30.0",
|
||||
"vue": "^3.5.22"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.1.4",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"postcss": "^8.4.49",
|
||||
"vite": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.0.0",
|
||||
"npm": ">= 9.0.0"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><html><head><meta charset=utf-8><title>个人简历</title><meta name=viewport content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><link href=/anires/public/static/css/app.97f24077373dbd5f86744ecd100255e7.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/anires/public/static/js/manifest.22a4ae013e2ceb48b7dd.js></script><script type=text/javascript src=/anires/public/static/js/vendor.045b08f12500b7884e7e.js></script><script type=text/javascript src=/anires/public/static/js/app.c4b2e14682ed119846b5.js></script></body></html>
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
#app[data-v-22ce9519] {
|
||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
html[data-v-22ce9519] {
|
||||
min-height: 100vh;
|
||||
}
|
||||
*[data-v-22ce9519] {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
pre[data-v-35c047fd]{
|
||||
}
|
||||
@media (max-width:500px){
|
||||
pre[data-v-35c047fd]{
|
||||
}
|
||||
}
|
||||
.code[data-v-35c047fd] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width:500px){
|
||||
.resumeEditor[data-v-647e008b]{
|
||||
}
|
||||
}
|
||||
.htmlMode[data-v-647e008b] {
|
||||
animation: flip 2s;
|
||||
}
|
||||
@keyframes flip {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width:500px){
|
||||
.thankEditor[data-v-29ad1df6]{
|
||||
}
|
||||
}
|
||||
.htmlMode[data-v-29ad1df6] {
|
||||
animation: flip 2s;
|
||||
}
|
||||
@keyframes flip {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
*{margin: 0; padding: 0;}
|
||||
*{box-sizing: border-box;}
|
||||
*::before{box-sizing: border-box;}
|
||||
*::after{box-sizing: border-box;}
|
||||
|
||||
#app[data-v-53c8d54c] {
|
||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
min-height: 100vh; position: relative;
|
||||
}
|
||||
html[data-v-53c8d54c] {
|
||||
min-height: 100vh;
|
||||
}
|
||||
*[data-v-53c8d54c]{
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
/*# sourceMappingURL=app.97f24077373dbd5f86744ecd100255e7.css.map*/
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["webpack:///./src/App.vue","webpack:///./src/components/StyleEditor.vue","webpack:///./src/components/ResumeEditor.vue","webpack:///./src/components/ThankEditor.vue","webpack:///./src/assets/reset.css","webpack:///./src/Mobile.vue"],"names":[],"mappings":";AACA;EACE,oDAAoD;EACpD,oCAAoC;EACpC,mCAAmC;CACpC;AACD;EACE,kBAAkB;CACnB;AACD;EACE,uBAAuB;CACxB;;ACVD;CACC;AACD;AACA;CACC;CACA;AACD;EACE,cAAc;CACf;;ACRD;AACA;CACC;CACA;AACD;EACE,mBAAmB;CACpB;AACD;AACA;IACI,WAAW;CACd;AACD;IACI,WAAW;CACd;CACA;;ACdD;AACA;CACC;CACA;AACD;EACE,mBAAmB;CACpB;AACD;AACA;IACI,WAAW;CACd;AACD;IACI,WAAW;CACd;CACA;ACfD,EAAE,UAAU,CAAC,WAAW,CAAC;AACzB,EAAE,uBAAuB,CAAC;AAC1B,UAAU,uBAAuB,CAAC;AAClC,SAAS,uBAAuB,CAAC;;ACFjC;EACE,oDAAoD;EACpD,oCAAoC;EACpC,mCAAmC;EACnC,kBAAkB,CAAC,mBAAmB;CACvC;AACD;EACE,kBAAkB;CACnB;AACD;EACE,uBAAuB;CACxB","file":"static/css/app.97f24077373dbd5f86744ecd100255e7.css","sourcesContent":["\n#app[data-v-22ce9519] {\n font-family: 'Avenir', Helvetica, Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\nhtml[data-v-22ce9519] {\n min-height: 100vh;\n}\n*[data-v-22ce9519] {\n box-sizing: border-box;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/App.vue","\npre[data-v-35c047fd]{\n}\n@media (max-width:500px){\npre[data-v-35c047fd]{\n}\n}\n.code[data-v-35c047fd] {\n display: none;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/StyleEditor.vue","\n@media (max-width:500px){\n.resumeEditor[data-v-647e008b]{\n}\n}\n.htmlMode[data-v-647e008b] {\n animation: flip 2s;\n}\n@keyframes flip {\nfrom {\n opacity: 0;\n}\nto {\n opacity: 1;\n}\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/ResumeEditor.vue","\n@media (max-width:500px){\n.thankEditor[data-v-29ad1df6]{\n}\n}\n.htmlMode[data-v-29ad1df6] {\n animation: flip 2s;\n}\n@keyframes flip {\nfrom {\n opacity: 0;\n}\nto {\n opacity: 1;\n}\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/ThankEditor.vue","*{margin: 0; padding: 0;}\n*{box-sizing: border-box;}\n*::before{box-sizing: border-box;}\n*::after{box-sizing: border-box;}\n\n\n\n// WEBPACK FOOTER //\n// ./src/assets/reset.css","\n#app[data-v-53c8d54c] {\n font-family: 'Avenir', Helvetica, Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n min-height: 100vh; position: relative;\n}\nhtml[data-v-53c8d54c] {\n min-height: 100vh;\n}\n*[data-v-53c8d54c]{\n box-sizing: border-box;\n}\n\n\n\n\n// WEBPACK FOOTER //\n// ./src/Mobile.vue"],"sourceRoot":""}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
!function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,c,i){for(var u,a,f,s=0,l=[];s<t.length;s++)a=t[s],o[a]&&l.push(o[a][0]),o[a]=0;for(u in c)Object.prototype.hasOwnProperty.call(c,u)&&(e[u]=c[u]);for(r&&r(t,c,i);l.length;)l.shift()();if(i)for(s=0;s<i.length;s++)f=n(n.s=i[s]);return f};var t={},o={2:0};n.e=function(e){function r(){u.onerror=u.onload=null,clearTimeout(a);var n=o[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),o[e]=void 0)}var t=o[e];if(0===t)return new Promise(function(e){e()});if(t)return t[2];var c=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=c;var i=document.getElementsByTagName("head")[0],u=document.createElement("script");u.type="text/javascript",u.charset="utf-8",u.async=!0,u.timeout=12e4,n.nc&&u.setAttribute("nonce",n.nc),u.src=n.p+"static/js/"+e+"."+{0:"045b08f12500b7884e7e",1:"c4b2e14682ed119846b5"}[e]+".js";var a=setTimeout(r,12e4);return u.onerror=u.onload=r,i.appendChild(u),c},n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="/anires/public/",n.oe=function(e){throw console.error(e),e}}([]);
|
||||
//# sourceMappingURL=manifest.22a4ae013e2ceb48b7dd.js.map
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+384
@@ -0,0 +1,384 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<div id="content">
|
||||
<StyleEditor ref="styleEditor" :code="currentStyle"></StyleEditor>
|
||||
<ResumeEditor ref="resumeEditor" :markdown="currentMarkdown" :enableHtml="enableHtml"></ResumeEditor>
|
||||
</div>
|
||||
<div id="foot">
|
||||
<ThankEditor ref="thankEditor" :markdown="currentThankMarkdown" :enableHtml="enableHtml"></ThankEditor>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
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,
|
||||
ThankEditor
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
interval: 1,
|
||||
currentStyle: '',
|
||||
enableHtml: false,
|
||||
fullStyle: [
|
||||
`/*
|
||||
* GitHub: https://github.com/hoelee/
|
||||
* Hi, I'm Mr. Hoelee — Web3 Developer & Full Stack Engineer
|
||||
* Let me create an animated resume!
|
||||
*/
|
||||
|
||||
/* Add transition effects to all elements */
|
||||
* {
|
||||
transition: all .1s;
|
||||
}
|
||||
/* Set background color */
|
||||
html {
|
||||
color: rgb(222,222,222); background: rgb(0,64,64);
|
||||
}
|
||||
#content{
|
||||
height:70vh;
|
||||
margin:0;
|
||||
}
|
||||
#foot{
|
||||
height:29vh;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
/* Set border */
|
||||
.styleEditor {
|
||||
padding: .5em;
|
||||
border: 1px solid;
|
||||
margin: .5em;
|
||||
overflow: auto;
|
||||
width: 50vw; height: 70vh;
|
||||
background: rgb(20,20,20);
|
||||
}
|
||||
/* Code highlighting */
|
||||
.token.selector{ color: rgb(130,150,0); }
|
||||
.token.property{ color: rgb(190,140,0); }
|
||||
.token.punctuation{ color: yellow; }
|
||||
.token.function{ color: rgb(40,160,150); }
|
||||
|
||||
/* Add 3D effect */
|
||||
html{
|
||||
perspective: 1000px;
|
||||
}
|
||||
.styleEditor {
|
||||
position: fixed; left: 0; top: 0;
|
||||
-webkit-transition: none;
|
||||
transition: none;
|
||||
-webkit-transform: rotateY(10deg) translateZ(-100px);
|
||||
transform: rotateY(10deg) translateZ(-100px);
|
||||
}
|
||||
|
||||
/* Prepare an editor */
|
||||
.resumeEditor{
|
||||
position: fixed; right: 0; top: 0;
|
||||
padding: .5em; margin: .5em;
|
||||
width: 50vw; height: 70vh;
|
||||
border: 1px solid;
|
||||
background: rgb(200,200,200); color: #222;
|
||||
overflow: auto;
|
||||
-webkit-transition: none;
|
||||
transition: none;
|
||||
-webkit-transform: rotateY(-10deg) translateZ(-100px);
|
||||
transform: rotateY(-10deg) translateZ(-100px);
|
||||
}
|
||||
/* Start writing resume */
|
||||
`, `
|
||||
/* Convert Markdown to HTML
|
||||
* then add some styles to HTML
|
||||
*/
|
||||
.resumeEditor{
|
||||
padding: 0.5em;
|
||||
}
|
||||
.resumeEditor h1{
|
||||
font-size: 1.3em;
|
||||
}
|
||||
.resumeEditor h2{
|
||||
font-size: 1.2em;
|
||||
display: inline-block;
|
||||
border-bottom: 1px solid;
|
||||
margin: 1em 0 .5em;
|
||||
}
|
||||
.resumeEditor ul,.resumeEditor ol{
|
||||
list-style: none;
|
||||
}
|
||||
.resumeEditor ul> li::before{
|
||||
content: '•';
|
||||
margin-right: .5em;
|
||||
}
|
||||
.resumeEditor ol {
|
||||
counter-reset: section;
|
||||
}
|
||||
.resumeEditor ol li::before {
|
||||
counter-increment: section;
|
||||
content: counters(section, ".") " ";
|
||||
margin-right: .5em;
|
||||
}
|
||||
.resumeEditor blockquote {
|
||||
margin: 1em;
|
||||
padding: .5em;
|
||||
background: #ddd;
|
||||
}
|
||||
`, `/* Write a thank you note.
|
||||
* Thanks for everyone's attention.
|
||||
*/
|
||||
.styleEditor{
|
||||
width:49vw;
|
||||
height:70vh;
|
||||
margin-left:1.2vw;
|
||||
}
|
||||
.resumeEditor{
|
||||
width:49vw;
|
||||
height:70vh;
|
||||
margin-right:1.2em;
|
||||
}
|
||||
|
||||
.thankEditor{
|
||||
position: relative; left: 0; top: 0;
|
||||
background: #E9D9BB;
|
||||
color: #001C42;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.thankEditor {
|
||||
width: 99vw; height: 45vh;
|
||||
border: 1px solid #ccc;
|
||||
font-size: .9em;
|
||||
}
|
||||
`,`
|
||||
.thankEditor{
|
||||
padding: .5em; margin: .5em; margin-top:1em;
|
||||
}
|
||||
|
||||
.thankEditor ul,.thankEditor ol{
|
||||
list-style: none;
|
||||
}
|
||||
.thankEditor ul> li::before{
|
||||
content: '☞'; color: red;
|
||||
margin-right: .5em;
|
||||
}
|
||||
.thankEditor ol {
|
||||
counter-reset: section;
|
||||
}
|
||||
.thankEditor ol li::before {
|
||||
counter-increment: section;
|
||||
content: counters(section, "☞") " ";
|
||||
margin-right: .5em;
|
||||
}
|
||||
|
||||
.thankEditor{
|
||||
width: 95vw;
|
||||
height: 45vh;
|
||||
margin: 0 auto;
|
||||
-webkit-transition: none;
|
||||
transition: none;
|
||||
-webkit-transform: rotateX(-10deg) translateZ(-200px);
|
||||
transform: rotateX(-10deg) translateZ(-200px);
|
||||
}
|
||||
|
||||
`],
|
||||
currentMarkdown: '',
|
||||
currentThankMarkdown: '',
|
||||
fullMarkdown: `Mr. Hoelee
|
||||
====
|
||||
Hi, I'm Mr. Hoelee — a Web3 Developer & Experience Full Stack Engineer.
|
||||
I design and build bridges between Web2 and Web3 — crafting scalable backend systems, gas-optimized smart contracts, and immersive decentralized frontends.
|
||||
My passion lies at the intersection of blockchain innovation, AI automation, and full-stack engineering — shaping how the next internet will work.
|
||||
|
||||
About Me
|
||||
----
|
||||
I'm a passionate Web3 developer with hands-on experience creating secure, upgradeable, and gas-efficient ERC-20 / ERC-721 smart contracts using Solidity, Hardhat, and Foundry.
|
||||
Beyond the blockchain, I bring an enterprise-level backend mindset — leveraging Java Spring Boot, CodeIgniter, and Dockerized infrastructure to scale decentralized apps with the reliability of traditional systems.
|
||||
When I'm not building, I explore the evolving Web3 landscape — from Layer 2 protocols and DAO governance to DeFi innovation and on-chain analytics.
|
||||
My mission: to merge creativity, code, and cryptography into meaningful digital experiences.
|
||||
|
||||
Web3 & Blockchain
|
||||
----
|
||||
- Solidity smart contracts (ERC-20 / ERC-721 / upgradeable patterns)
|
||||
- Hardhat, Foundry testing, OpenZeppelin & Chainlink Oracles
|
||||
- Alchemy SDK, Polygon, IPFS integration
|
||||
|
||||
Frontend Development
|
||||
----
|
||||
- Web3.js / Ethers.js integration
|
||||
- JavaScript, jQuery, HTML5, CSS3
|
||||
- UI/UX prototyping with Photoshop, Illustrator, Canva
|
||||
|
||||
Backend & DevOps
|
||||
----
|
||||
- PHP CodeIgniter (Web2↔Web3 bridge)
|
||||
- Java Spring Boot + Hibernate for scalable data layers
|
||||
- Databases: MySQL, MongoDB, Memcached, Elasticsearch
|
||||
- Docker, Nginx, IIS, OpenLiteSpeed, Apache Tomcat, WordPress
|
||||
- Server management (Windows, Ubuntu, unRaid, DSM)
|
||||
|
||||
Work Ethic
|
||||
----
|
||||
- Effective Communicator: Strong written & verbal clarity in remote teams
|
||||
- Problem-Solver: Adaptive mindset for complex, high-speed development
|
||||
- Continuous Learner: Always exploring blockchain advancements
|
||||
- AI-Driven: Using AI to accelerate workflows and creative tasks
|
||||
- Client-Focused: Prioritizing real solutions and transparent delivery
|
||||
|
||||
Education - Bachelor of Information System (Hons)
|
||||
----
|
||||
- Universiti Tunku Abdul Rahman (UTAR), Kampar, Malaysia
|
||||
- March 2016 | CGPA: 3.06 / 4.00 - Degree In Merit
|
||||
- Download Certificate → [hoelee.com/cert](https://hoelee.com/cert)
|
||||
|
||||
Languages
|
||||
----
|
||||
English | Malay | Mandarin | Cantonese | Hokkien
|
||||
|
||||
Interests
|
||||
----
|
||||
- DeFi Innovation
|
||||
- DAO Governance
|
||||
- AI-assisted Development
|
||||
- Cryptography
|
||||
- On-Chain Analytics
|
||||
|
||||
|
||||
`, thanksMarkdown: `
|
||||
Thank you for reading!
|
||||
----
|
||||
|
||||
I'm open to remote collaborations and Web3-driven projects.
|
||||
|
||||
Let's build something that connects the decentralized future with real-world impact.
|
||||
|
||||
* WhatsApp Mr Hoelee Now → [+6012-7972969](https://wa.me/60127972969)
|
||||
* Email Mr Hoelee Now → [[email protected]](mailto:[email protected])
|
||||
* Passionate about bridging the gap between traditional systems and decentralized technologies
|
||||
* Ready to contribute to innovative Web3 projects that solve real-world problems
|
||||
* Available for remote collaboration with forward-thinking teams and startups
|
||||
* Let's discuss how we can leverage blockchain technology to create meaningful digital experiences
|
||||
|
||||
Get in touch to explore opportunities in:
|
||||
* DeFi protocol development
|
||||
* Smart contract architecture and optimization
|
||||
* Web3 frontend integration
|
||||
* Scalable backend systems for dApps
|
||||
* Cross-chain solutions and Layer 2 implementations
|
||||
|
||||
`
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.makeResume()
|
||||
},
|
||||
|
||||
methods: {
|
||||
makeResume: async function () {
|
||||
await this.progressivelyShowStyle(0);
|
||||
await this.progressivelyShowResume();
|
||||
await this.progressivelyShowStyle(1);
|
||||
await this.showHtml();
|
||||
await this.progressivelyShowStyle(1);
|
||||
await this.progressivelyShowThanks();
|
||||
await this.progressivelyShowStyle(1)
|
||||
},
|
||||
showHtml() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.enableHtml = true
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
progressivelyShowStyle(n) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let interval = this.interval
|
||||
let showStyle = (async function () {
|
||||
let style = this.fullStyle[n]
|
||||
if (!style) {
|
||||
return
|
||||
}
|
||||
// Calculate total character count of first n styles
|
||||
let length = this.fullStyle.filter((_, index) => index <= n).map((item) => item.length).reduce((p, c) => p + c, 0)
|
||||
let prefixLength = length - style.length
|
||||
if (this.currentStyle.length < length) {
|
||||
let l = this.currentStyle.length - prefixLength
|
||||
let char = style.substring(l, l + 1) || ' '
|
||||
this.currentStyle += char
|
||||
if (style.substring(l - 1, l) === '\n' && this.$refs.styleEditor) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.styleEditor.goBottom()
|
||||
})
|
||||
}
|
||||
setTimeout(showStyle, interval)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
}).bind(this)
|
||||
showStyle()
|
||||
})
|
||||
},
|
||||
progressivelyShowResume() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let length = this.fullMarkdown.length
|
||||
let interval = this.interval
|
||||
let showResume = () => {
|
||||
if (this.currentMarkdown.length < length) {
|
||||
this.currentMarkdown = this.fullMarkdown.substring(0, this.currentMarkdown.length + 1)
|
||||
let lastChar = this.currentMarkdown[this.currentMarkdown.length - 1]
|
||||
let prevChar = this.currentMarkdown[this.currentMarkdown.length - 2]
|
||||
if (prevChar === '\n' && this.$refs.resumeEditor) {
|
||||
this.$nextTick(() => this.$refs.resumeEditor.goBottom())
|
||||
}
|
||||
setTimeout(showResume, interval)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
showResume()
|
||||
})
|
||||
},
|
||||
progressivelyShowThanks() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let length = this.thanksMarkdown.length
|
||||
let interval = this.interval
|
||||
let showThanks = () => {
|
||||
if (this.currentThankMarkdown.length < length) {
|
||||
this.currentThankMarkdown = this.thanksMarkdown.substring(0, this.currentThankMarkdown.length + 1)
|
||||
let lastChar = this.currentThankMarkdown[this.currentThankMarkdown.length - 1]
|
||||
let prevChar = this.currentThankMarkdown[this.currentThankMarkdown.length - 2]
|
||||
if (prevChar === '\n' && this.$refs.thankEditor) {
|
||||
this.$nextTick(() => this.$refs.thankEditor.goBottom())
|
||||
}
|
||||
setTimeout(showThanks, interval)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
showThanks()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#app {
|
||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
html {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<StyleEditor ref="styleEditor" :code="currentStyle"></StyleEditor>
|
||||
<ResumeEditor ref="resumeEditor" :markdown="currentMarkdown" :enableHtml="enableHtml"></ResumeEditor>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import StyleEditor from './components/StyleEditor.vue'
|
||||
import ResumeEditor from './components/ResumeEditor.vue'
|
||||
import './assets/reset.css'
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components: {
|
||||
StyleEditor,
|
||||
ResumeEditor
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
interval: 1,
|
||||
currentStyle: '',
|
||||
enableHtml: false,
|
||||
fullStyle: [
|
||||
`/*
|
||||
* GitHub: https://github.com/hoelee/
|
||||
* Hi, I'm Mr. HoeleeWeb3 Developer & Full Stack Engineer
|
||||
* Let me create an animated resume!
|
||||
*/
|
||||
|
||||
/* Add transition effects to all elements */
|
||||
* {
|
||||
transition: all .2s;
|
||||
}
|
||||
/* Set background color */
|
||||
html {
|
||||
color: rgb(222,222,222);
|
||||
background: rgb(0,43,54);
|
||||
}
|
||||
/* Set border */
|
||||
.styleEditor {
|
||||
padding: .5em;
|
||||
border: 1px solid;
|
||||
overflow: auto;
|
||||
width: 96vw;
|
||||
margin: 2.3vh 2vw;
|
||||
height: 90vh;
|
||||
}
|
||||
/* Too short */
|
||||
.styleEditor {
|
||||
height: 47vh;
|
||||
}
|
||||
/* Code highlighting */
|
||||
.token.selector{
|
||||
color: rgb(133,153,0);
|
||||
}
|
||||
.token.property{
|
||||
color: rgb(187,137,0);
|
||||
}
|
||||
.token.punctuation{
|
||||
color: yellow;
|
||||
}
|
||||
.token.function{
|
||||
color: rgb(42,161,152);
|
||||
}
|
||||
|
||||
/* Add 3D effect */
|
||||
html{
|
||||
perspective: 1000px;
|
||||
}
|
||||
.styleEditor {
|
||||
position: fixed; left: 0; top: 0;
|
||||
transform: rotateX(-10deg) translateZ(-50px) ;
|
||||
}
|
||||
|
||||
/* Prepare an editor */
|
||||
.resumeEditor{
|
||||
position: fixed;
|
||||
top: 48%; left: 0;
|
||||
padding: .5em; margin: 1.5vh;
|
||||
width: 94vw; height: 48.5vh;
|
||||
border: 1px solid;
|
||||
background: white; color: #222;
|
||||
overflow: auto;
|
||||
}
|
||||
/* Start writing resume */
|
||||
|
||||
|
||||
`,
|
||||
`
|
||||
/* Convert Markdown to HTML
|
||||
* then add some styles to HTML
|
||||
*/
|
||||
.resumeEditor{
|
||||
padding: 1em;
|
||||
}
|
||||
.resumeEditor h1{
|
||||
font-size: 1.3em;
|
||||
}
|
||||
.resumeEditor h2{
|
||||
font-size: 1.2em;
|
||||
display: inline-block;
|
||||
border-bottom: 1px solid;
|
||||
margin: 1em 0 .5em;
|
||||
}
|
||||
.resumeEditor ul,.resumeEditor ol{
|
||||
list-style: none;
|
||||
}
|
||||
.resumeEditor ul> li::before{
|
||||
content: '•';
|
||||
margin-right: .5em;
|
||||
}
|
||||
.resumeEditor ol {
|
||||
counter-reset: section;
|
||||
}
|
||||
.resumeEditor ol li::before {
|
||||
counter-increment: section;
|
||||
content: counters(section, ".") " ";
|
||||
margin-right: .5em;
|
||||
}
|
||||
.resumeEditor blockquote {
|
||||
margin: 1em;
|
||||
padding: .5em;
|
||||
background: #ddd;
|
||||
}
|
||||
`, `
|
||||
/* Add mobile-optimized styles */
|
||||
.resumeEditor {
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.resumeEditor h1 {
|
||||
color: #0066cc;
|
||||
text-align: center;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.resumeEditor a {
|
||||
color: #0066cc;
|
||||
text-decoration: none;
|
||||
}
|
||||
.resumeEditor a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`],
|
||||
currentMarkdown: '',
|
||||
fullMarkdown: `Mr. Hoelee
|
||||
====
|
||||
Hi, I'm Mr. Hoelee — a Web3 Developer & Experience Full Stack Engineer.
|
||||
|
||||
I design and build bridges between Web2 and Web3 — crafting scalable backend systems, gas-optimized smart contracts, and immersive decentralized frontends.
|
||||
|
||||
My passion lies at the intersection of blockchain innovation, AI automation, and full-stack engineering — shaping how the next internet will work.
|
||||
|
||||
About Me
|
||||
----
|
||||
I'm a passionate Web3 developer with hands-on experience creating secure, upgradeable, and gas-efficient ERC-20 / ERC-721 smart contracts using Solidity, Hardhat, and Foundry.
|
||||
|
||||
Beyond the blockchain, I bring an enterprise-level backend mindset — leveraging Java Spring Boot, CodeIgniter, and Dockerized infrastructure to scale decentralized apps with the reliability of traditional systems.
|
||||
|
||||
When I'm not building, I explore the evolving Web3 landscape — from Layer 2 protocols and DAO governance to DeFi innovation and on-chain analytics.
|
||||
|
||||
My mission: to merge creativity, code, and cryptography into meaningful digital experiences.
|
||||
|
||||
Web3 & Blockchain
|
||||
----
|
||||
- Solidity smart contracts (ERC-20 / ERC-721 / upgradeable patterns)
|
||||
- Hardhat, Foundry testing, OpenZeppelin & Chainlink Oracles
|
||||
- Alchemy SDK, Polygon, IPFS integration
|
||||
|
||||
Frontend Development
|
||||
----
|
||||
- Web3.js / Ethers.js integration
|
||||
- JavaScript, jQuery, HTML5, CSS3
|
||||
- UI/UX prototyping with Photoshop, Illustrator, Canva
|
||||
|
||||
Backend & DevOps
|
||||
----
|
||||
- PHP CodeIgniter (Web2↔Web3 bridge)
|
||||
- Java Spring Boot + Hibernate for scalable data layers
|
||||
- Databases: MySQL, MongoDB, Memcached, Elasticsearch
|
||||
- Docker, Nginx, IIS, OpenLiteSpeed, Apache Tomcat, WordPress
|
||||
- Server management (Windows, Ubuntu, unRaid, DSM)
|
||||
|
||||
Work Ethic
|
||||
----
|
||||
- Effective Communicator: Strong written & verbal clarity in remote teams
|
||||
- Problem-Solver: Adaptive mindset for complex, high-speed development
|
||||
- Continuous Learner: Always exploring blockchain advancements
|
||||
- AI-Driven: Using AI to accelerate workflows and creative tasks
|
||||
- Client-Focused: Prioritizing real solutions and transparent delivery
|
||||
|
||||
Education - Bachelor of Information System (Hons)
|
||||
----
|
||||
- Universiti Tunku Abdul Rahman (UTAR), Kampar, Malaysia
|
||||
- March 2016 | CGPA: 3.06 / 4.00 - Degree In Merit
|
||||
- Download Certificate → [hoelee.com/cert](https://hoelee.com/cert)
|
||||
|
||||
Languages
|
||||
----
|
||||
English | Malay | Mandarin | Cantonese | Hokkien
|
||||
|
||||
Interests
|
||||
----
|
||||
- DeFi Innovation
|
||||
- DAO Governance
|
||||
- AI-assisted Development
|
||||
- Cryptography
|
||||
- On-Chain Analytics
|
||||
|
||||
Contact Me
|
||||
----
|
||||
I'm open to remote collaborations and Web3-driven projects.
|
||||
|
||||
Let's build something that connects the decentralized future with real-world impact.
|
||||
|
||||
* WhatsApp Mr Hoelee Now → [+6012-7972969](https://wa.me/60127972969)
|
||||
* Email Mr Hoelee Now → [[email protected]](mailto:[email protected])
|
||||
* Ready to contribute to innovative Web3 projects that solve real-world problems
|
||||
* Available for remote collaboration with forward-thinking teams and startups
|
||||
|
||||
Get in touch to explore opportunities in:
|
||||
* DeFi protocol development
|
||||
* Smart contract architecture and optimization
|
||||
* Web3 frontend integration
|
||||
* Scalable backend systems for dApps
|
||||
* Cross-chain solutions and Layer 2 implementations
|
||||
|
||||
`
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.makeResume()
|
||||
},
|
||||
|
||||
methods: {
|
||||
makeResume: async function () {
|
||||
await this.progressivelyShowStyle(0)
|
||||
await this.progressivelyShowResume()
|
||||
await this.progressivelyShowStyle(1)
|
||||
await this.showHtml()
|
||||
await this.progressivelyShowStyle(1)
|
||||
},
|
||||
showHtml: function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.enableHtml = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.resumeEditor.goTop()
|
||||
})
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
progressivelyShowStyle(n) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let interval = this.interval
|
||||
let showStyle = (async function () {
|
||||
let style = this.fullStyle[n]
|
||||
if (!style) { return }
|
||||
// Calculate total character count of first n styles
|
||||
let length = this.fullStyle.filter((_, index) => index <= n).map((item) => item.length).reduce((p, c) => p + c, 0)
|
||||
let prefixLength = length - style.length
|
||||
if (this.currentStyle.length < length) {
|
||||
let l = this.currentStyle.length - prefixLength
|
||||
let char = style.substring(l, l + 1) || ' '
|
||||
this.currentStyle += char
|
||||
if (style.substring(l - 1, l) === '\n' && this.$refs.styleEditor) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.styleEditor.goBottom()
|
||||
})
|
||||
}
|
||||
setTimeout(showStyle, interval)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
}).bind(this)
|
||||
showStyle()
|
||||
})
|
||||
},
|
||||
progressivelyShowResume() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let length = this.fullMarkdown.length
|
||||
let interval = this.interval
|
||||
let showResume = () => {
|
||||
if (this.currentMarkdown.length < length) {
|
||||
this.currentMarkdown = this.fullMarkdown.substring(0, this.currentMarkdown.length + 1)
|
||||
let lastChar = this.currentMarkdown[this.currentMarkdown.length - 1]
|
||||
let prevChar = this.currentMarkdown[this.currentMarkdown.length - 2]
|
||||
if (prevChar === '\n' && this.$refs.resumeEditor) {
|
||||
this.$nextTick(() => this.$refs.resumeEditor.goBottom())
|
||||
}
|
||||
setTimeout(showResume, interval)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
showResume()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#app {
|
||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
min-height: 100vh; position: relative;
|
||||
}
|
||||
|
||||
html {
|
||||
min-height: 100vh;
|
||||
}
|
||||
*{
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
</style>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
@@ -0,0 +1,4 @@
|
||||
*{margin: 0; padding: 0;}
|
||||
*{box-sizing: border-box;}
|
||||
*::before{box-sizing: border-box;}
|
||||
*::after{box-sizing: border-box;}
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="resumeEditor" :class="{htmlMode:enableHtml}" ref="container">
|
||||
<div v-if="enableHtml" v-html="result"></div>
|
||||
<pre v-else>{{result}}</pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { marked } from 'marked'
|
||||
export default {
|
||||
props: ['markdown', 'enableHtml'],
|
||||
name: 'ResumeEditor',
|
||||
computed: {
|
||||
result: function () {
|
||||
return this.enableHtml ? marked(this.markdown) : this.markdown
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goBottom: function () {
|
||||
this.$refs.container.scrollTop = 100000
|
||||
},
|
||||
goTop: function(){
|
||||
this.$refs.container.scrollTop = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@media (max-width:500px){
|
||||
.resumeEditor{
|
||||
}
|
||||
}
|
||||
.htmlMode {
|
||||
animation: flip 2s;
|
||||
}
|
||||
|
||||
@keyframes flip {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="styleEditor" ref="container">
|
||||
<div class="code" v-html="codeInStyleTag"></div>
|
||||
<pre class="" v-html="highlightedCode"></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Prism from 'prismjs'
|
||||
export default {
|
||||
name: 'Editor',
|
||||
props: ['code'],
|
||||
computed: {
|
||||
highlightedCode: function () {
|
||||
return Prism.highlight(this.code, Prism.languages.css)
|
||||
},
|
||||
codeInStyleTag: function () {
|
||||
return `<style>${this.code}</style>`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goBottom() {
|
||||
this.$refs.container.scrollTop = 100000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
pre{
|
||||
}
|
||||
@media (max-width:500px){
|
||||
pre{
|
||||
}
|
||||
}
|
||||
.code {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="thankEditor" :class="{htmlMode:enableHtml}" ref="container">
|
||||
<div v-if="enableHtml" v-html="result"></div>
|
||||
<pre v-else>{{result}}</pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { marked } from 'marked'
|
||||
export default {
|
||||
props: ['markdown', 'enableHtml'],
|
||||
name: 'ThankEditor',
|
||||
computed: {
|
||||
result: function () {
|
||||
return this.enableHtml ? marked(this.markdown) : this.markdown
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goBottom: function () {
|
||||
this.$refs.container.scrollTop = 100000
|
||||
},
|
||||
goTop: function(){
|
||||
this.$refs.container.scrollTop = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@media (max-width:500px){
|
||||
.thankEditor{
|
||||
}
|
||||
}
|
||||
.htmlMode {
|
||||
animation: flip 2s;
|
||||
}
|
||||
|
||||
@keyframes flip {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,7 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import Mobile from './Mobile.vue'
|
||||
|
||||
const width = document.documentElement.clientWidth
|
||||
const app = createApp(width > 500 ? App : Mobile)
|
||||
app.mount('#app')
|
||||
@@ -0,0 +1,31 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { resolve } from 'path'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src'),
|
||||
'src': resolve(__dirname, 'src'),
|
||||
'assets': resolve(__dirname, 'src/assets'),
|
||||
'components': resolve(__dirname, 'src/components')
|
||||
}
|
||||
},
|
||||
server: {
|
||||
port: 8080,
|
||||
open: true
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
assetsDir: 'static',
|
||||
rollupOptions: {
|
||||
output: {
|
||||
chunkFileNames: 'static/js/[name]-[hash].js',
|
||||
entryFileNames: 'static/js/[name]-[hash].js',
|
||||
assetFileNames: 'static/[ext]/[name]-[hash].[ext]'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user