(add) index page

This commit is contained in:
kakkokari-gtyih 2023-07-09 02:23:27 +09:00
parent 8e52c51c49
commit bff4e03643
59 changed files with 2096 additions and 48 deletions

View File

@ -3,6 +3,8 @@ import type { Graph, Thing } from 'schema-dts';
const { t, locale } = useI18n();
const route = useRoute();
const colorMode = useColorMode();
const getDescription = (): string => {
if (route.meta.description != null && route.meta.description != "") {
return route.meta.description;
@ -82,7 +84,7 @@ useHead((): Record<string, any> => ({
}));
</script>
<template>
<div>
<div class="text-slate-700 dark:text-slate-200 bg-slate-100 dark:bg-gray-900">
<noscript class="block bg-accent-800 text-white text-center py-1.5 px-3 keep-all relative z-[10005]">Please turn on Javascript from your browser's settings.</noscript>
<NuxtLayout>
<NuxtPage />

View File

@ -1,3 +1,24 @@
.bi::before,
[class^="bi-"]::before,
[class*=" bi-"]::before,
.bi {
display: inline-block;
font-family: bootstrap-icons !important;
font-style: normal;
font-weight: normal !important;
font-variant: normal;
text-transform: none;
line-height: 1;
vertical-align: -.125em;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.bi {
width: 1em;
height: 1em;
}
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind utilities;

47
assets/data/nav.ts Normal file
View File

@ -0,0 +1,47 @@
import { FunctionalComponent } from "nuxt/dist/app/compat/capi";
import GHIcon from "bi/github.svg";
/** ナビゲーションバー アイテム */
type NavItem = {
/** 翻訳キー */
i18n: string;
/** リンク先 */
to: string;
} | {
/** アイコンsvgをインポートして貼り付け */
icon: FunctionalComponent;
/** リンク先 */
to: string;
};
/** ナビゲーションバー コンテンツ */
export default <{
/** ナビゲーションの真ん中のリンク */
center: NavItem[];
/**
*
* SNSとかGithubのリンクとか
*/
right: NavItem[];
}> {
center: [
{
i18n: '_nav.servers',
to: '/servers/',
},
{
i18n: '_nav.docs',
to: '/docs/',
},
{
i18n: '_nav.blog',
to: '/blog/',
}
],
right: [
{
icon: GHIcon,
to: 'https://github.com/misskey-dev/misskey-hub',
},
]
};

24
assets/js/fadein/index.ts Normal file
View File

@ -0,0 +1,24 @@
export const vFadeIn = {
mounted: (src, binding, vn) => {
src.classList.add('__v_fadeIn_out');
src.children[0].style.transition = `all 0.5s ease`;
function onIntersect(entries) {
for (const entry of entries) {
if (entry.isIntersecting) {
entry.target.classList.add('__v_fadeIn_in');
} else {
entry.target.classList.remove('__v_fadeIn_in');
}
}
}
const observer = new IntersectionObserver(onIntersect, {
root: null,
rootMargin: '9999px 0px -300px 0px',
threshold: 0,
});
observer.observe(src);
}
};

View File

@ -0,0 +1,9 @@
export const vParallax = {
mounted: (src: HTMLElement, binding: Ref<number>) => {
src.style.willChange = 'transform';
window.addEventListener('scroll', () => {
src.style.transform = `translateY(${window.scrollY / binding.value}px)`;
}, { passive: true });
}
}

View File

@ -0,0 +1,84 @@
import * as THREE from 'three';
import * as Calc from './utils/calc';
import * as Ease from './utils/ease';
import { Loader } from './loader';
import { System } from './system';
export class Drop {
private loader: Loader;
private system: System;
private array: Drop[];
private group: THREE.Object3D;
private x: number;
private y: number;
private z: number;
private size: number;
private color: number;
private opacity: number;
private strength: number;
private yBase: number;
private progress: number = 0;
private rate: number = 0.01;
private geometry: System['boxGeometry'];
private material: THREE.MeshBasicMaterial;
private mesh: THREE.Mesh;
constructor(config, system) {
this.system = system;
this.loader = this.system.loader;
this.array = config.array;
this.group = config.group;
this.x = config.x;
this.y = config.y;
this.z = config.z;
this.size = config.size;
this.color = config.color;
this.opacity = config.opacity;
this.strength = config.strength;
this.yBase = config.y;
this.createMesh();
}
createMesh() {
this.geometry = this.system.boxGeometry;
this.material = new THREE.MeshBasicMaterial({
color: this.color,
transparent: true,
opacity: this.opacity,
depthTest: false,
precision: 'lowp'
});
this.mesh = new THREE.Mesh(this.geometry, this.material);
this.mesh.position.x = this.x;
this.mesh.position.y = this.y;
this.mesh.position.z = this.z;
this.mesh.scale.set(this.size, this.size, this.size);
this.group.add(this.mesh);
}
update(i) {
this.progress += this.rate * this.loader.deltaTimeNormal;
this.mesh.position.y = this.yBase - Ease.inExpo(this.progress, 0, 1, 1) * this.yBase;
this.mesh.scale.set(this.size, this.size + this.size * 16 * Ease.inExpo(this.progress, 0, 1, 1), this.size);
this.mesh.material.opacity = Ease.inExpo(this.progress, 0, 1, 1);
if(this.progress >= 1) {
this.geometry.dispose();
this.material.dispose();
this.group.remove(this.mesh);
this.array.splice(i, 1);
this.system.createRipple(this.mesh.position.x, this.mesh.position.z, this.strength);
}
}
}

View File

@ -0,0 +1,117 @@
import * as THREE from 'three';
import tinycolor from 'tinycolor2';
import { System } from './system';
export class Loader {
public timescale: number = 0.75;
public camera: THREE.PerspectiveCamera;
public clock: THREE.Clock;
public deltaTimeSeconds: number;
public deltaTimeMilliseconds: number;
public deltaTimeNormal: number;
public elapsedMilliseconds: number;
public system: System;
public scene: THREE.Scene;
public renderer: THREE.WebGLRenderer;
private diffTime: number;
private raf: ReturnType<typeof window.requestAnimationFrame> | null;
private dom: HTMLElement;
constructor(container: HTMLElement) {
this.dom = container;
this.raf = null;
this.setupTime();
this.setupScene();
this.setupCamera();
this.setupRenderer();
this.listen();
this.onResize();
this.system = new System(this, {
particleColor: tinycolor(getComputedStyle(this.dom).getPropertyValue('--c-text')).toHexString(),
dropColor: tinycolor(getComputedStyle(this.dom).getPropertyValue('--c-brand')).toHexString(),
rippleColor: tinycolor(getComputedStyle(this.dom).getPropertyValue('--c-brand')).toHexString(),
});
this.loop();
}
setupTime() {
this.clock = new THREE.Clock();
this.deltaTimeSeconds = this.clock.getDelta() * this.timescale;
this.deltaTimeMilliseconds = this.deltaTimeSeconds * 1000;
this.deltaTimeNormal = this.deltaTimeMilliseconds / (1000 / 60);
this.elapsedMilliseconds = 0;
}
setupScene() {
this.scene = new THREE.Scene();
}
setupCamera() {
this.camera = new THREE.PerspectiveCamera(70, 0, 0.0001, 10000);
//this.camera.position.x = 0;
//this.camera.position.y = 0;
//this.camera.position.z = 20;
this.camera.position.x = 0;
this.camera.position.y = -30;
this.camera.position.z = 0;
}
setupRenderer() {
this.renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
});
this.dom.appendChild(this.renderer.domElement);
}
update() {
this.deltaTimeSeconds = this.clock.getDelta();
if(this.diffTime) {
this.deltaTimeSeconds -= this.diffTime;
this.diffTime = 0;
}
this.deltaTimeSeconds *= this.timescale;
this.deltaTimeMilliseconds = this.deltaTimeSeconds * 1000;
this.deltaTimeNormal = this.deltaTimeMilliseconds / (1000 / 60);
this.elapsedMilliseconds += this.deltaTimeMilliseconds;
this.system.update();
}
render() {
this.renderer.render(this.scene, this.camera);
}
listen() {
window.addEventListener('resize', () => this.onResize());
}
onResize() {
const width = this.dom.offsetWidth;
const height = this.dom.offsetHeight;
this.camera.aspect = width / height;
this.camera.updateProjectionMatrix();
this.renderer.setPixelRatio(window.devicePixelRatio > 1 ? 2 : 1);
this.renderer.setSize(width, height);
}
loop() {
this.update();
this.render();
this.raf = window.requestAnimationFrame(() => this.loop());
}
destroy() {
if (this.raf) {
window.cancelAnimationFrame(this.raf);
}
}
}

View File

@ -0,0 +1,82 @@
import * as THREE from 'three';
import * as Calc from './utils/calc';
import * as Ease from './utils/ease';
import { Loader } from './loader';
import { System } from './system';
export class Particle {
private loader: Loader;
private system: System;
private lerpFactor = 0.3;
private dampFactor = 0.3;
private group: THREE.Object3D;
private x: number;
private y: number;
private z: number;
private size: number;
private color: number;
private opacity: number;
public base: THREE.Vector3;
public velocity: THREE.Vector3;
private geometry: THREE.SphereBufferGeometry;
private material: THREE.MeshBasicMaterial;
private mesh: THREE.Mesh;
constructor(config, system) {
this.system = system;
this.loader = this.system.loader;
this.group = config.group;
this.x = config.x;
this.y = config.y;
this.z = config.z;
this.size = config.size;
this.color = config.color;
this.opacity = config.opacity;
this.createMesh();
this.base = new THREE.Vector3(config.x, config.y, config.z);
this.velocity = new THREE.Vector3(0, 0, 0);
}
createMesh() {
this.geometry = this.system.sphereGeometry;
this.material = new THREE.MeshBasicMaterial({
color: this.color,
transparent: true,
opacity: this.opacity,
depthTest: false,
precision: 'lowp'
});
this.mesh = new THREE.Mesh(this.geometry, this.material);
this.mesh.position.x = this.x;
this.mesh.position.y = this.y;
this.mesh.position.z = this.z;
this.mesh.scale.set(this.size, this.size, this.size);
this.group.add(this.mesh);
}
update() {
const scale = 0.075 + (Math.abs(this.velocity.y) / 25)
this.mesh.scale.set(scale, scale, scale);
//const opacity = 0.15 + (Math.abs(this.velocity.y) / 1)
//this.mesh.material.opacity = Calc.clamp(opacity, 0.15, 1);
const opacity = 0 + (Math.abs(this.velocity.y) / 1)
this.mesh.material.opacity = Calc.clamp(opacity, 0, 1);
this.velocity.y += (this.base.y - this.mesh.position.y) * this.lerpFactor;
this.velocity.multiplyScalar(this.dampFactor);
this.mesh.position.add(this.velocity);
}
}

View File

@ -0,0 +1,95 @@
import * as THREE from 'three';
import { MeshLineGeometry as MeshLine, MeshLineMaterial } from 'meshline';
import * as Calc from './utils/calc';
import * as Ease from './utils/ease';
import { Loader } from './loader';
import { System } from './system';
export class Ripple {
private loader: Loader;
private system: System;
private array: Ripple[];
private group: THREE.Object3D;
private sphere: THREE.Sphere;
private strength: number; // 波の高さ
private threshold: number;
private growth: number;
private life: number;
private decay: number;
private influence: THREE.Vector3;
private geometry: MeshLine;
private material: THREE.LineBasicMaterial;
private mesh: THREE.Mesh;
constructor(config, system) {
this.system = system;
this.loader = this.system.loader;
this.array = config.array;
this.group = config.group;
this.sphere = new THREE.Sphere(new THREE.Vector3(config.x, config.y, config.z), 0);
this.strength = config.strength ? config.strength : Calc.rand(7, 15);
this.threshold = Calc.rand(5, 20);
this.growth = Calc.rand(0.2, 0.5);
this.life = 1;
this.decay = Calc.rand(0.01, 0.02);
this.influence = new THREE.Vector3();
const points = [];
const resolution = 64;
for (let j = 0; j < (Math.PI * 2) + ((Math.PI) / resolution); j += (2 * Math.PI) / resolution) {
points.push(Math.cos(j), Math.sin(j), 0);
}
this.geometry = new MeshLine();
this.geometry.setPoints(points);
this.material = new MeshLineMaterial({
lineWidth: 0.25,
color: config.color,
opacity: 1,
transparent: true,
depthTest: false,
});
this.material.onBeforeRender = () => {};
this.mesh = new THREE.Mesh(this.geometry, this.material);
this.mesh.rotation.x = Math.PI / 2;
this.mesh.position.x = this.sphere.center.x;
this.mesh.position.y = 0;
this.mesh.position.z = this.sphere.center.z;
this.group.add(this.mesh);
}
getInfluenceVector(vec) {
this.influence.set(0, 0, 0);
let distance = this.sphere.distanceToPoint(vec);
if(distance <= this.threshold ) {
let ease = Ease.inOutSine(this.threshold - distance, 0, 1, this.threshold);
let power = (this.strength * ease * this.life);
this.influence.addVectors(vec, this.sphere.center).multiplyScalar(power);
}
return this.influence;
}
update(i) {
this.sphere.radius += (this.growth * this.life) * this.loader.deltaTimeNormal;
this.life -= this.decay * this.loader.deltaTimeNormal;
this.mesh.position.y = (1 - this.life) * -2;
let newScale = 0.001 + this.sphere.radius;
this.mesh.scale.set(newScale, newScale, newScale);
this.mesh.material.opacity = this.life / 3;
if(this.life <= 0) {
this.destroy(i);
}
}
destroy(i) {
this.geometry.dispose();
this.material.dispose();
this.group.remove(this.mesh);
this.array.splice(i, 1);
}
}

View File

@ -0,0 +1,152 @@
import * as THREE from 'three';
import * as Calc from './utils/calc';
import * as Ease from './utils/ease';
import { Drop } from './drop';
import { Loader } from './loader';
import { Particle } from './particle';
import { Ripple } from './ripple';
function hexToInt(rrggbb: string): number {
if (rrggbb.startsWith('#')) rrggbb = rrggbb.substr(1);
return parseInt(rrggbb, 16);
}
export class System {
public loader: Loader;
private drops: Drop[] = [];
private ripples: Ripple[] = [];
private particles: Particle[] = [];
public sphereGeometry: THREE.SphereGeometry;
public boxGeometry: THREE.BoxGeometry;
private center: THREE.Vector3;
private particleGroup: THREE.Object3D;
private size = 96;
private cols = 32;
private rows = 32;
private tick: number = 0;
private dropTick = 0;
private dropTickMin = 25;
private dropTickMax = 30;
private particleColor: number;
private rippleColor: number;
private dropColor: number;
constructor(loader, config: { particleColor: string; rippleColor: string; dropColor: string; }) {
this.loader = loader;
this.particleColor = hexToInt(config.particleColor);
this.rippleColor = hexToInt(config.rippleColor);
this.dropColor = hexToInt(config.dropColor);
this.sphereGeometry = new THREE.SphereGeometry(1, 16, 16);
this.boxGeometry = new THREE.BoxGeometry(1, 1, 1);
this.center = new THREE.Vector3();
this.loader.camera.lookAt(this.center);
this.particleGroup = new THREE.Object3D();
this.particleGroup.scale.set(1, 1, 1);
this.loader.scene.add(this.particleGroup);
for(let col = 0; col < this.cols; col++) {
for(let row = 0; row < this.rows; row++) {
let x = Calc.map(col, 0, this.cols - 1, -this.size / 2, this.size / 2);
let y = 0;
let z = Calc.map(row, 0, this.rows - 1, -this.size / 2, this.size / 2);
this.particles.push(new Particle({
group: this.particleGroup,
x: x,
y: y,
z: z,
size: 0.01,
color: this.particleColor,
opacity: 0.01
}, this));
}
}
}
createDrop(x?, y?, z?, strength?) {
this.drops.push(new Drop({
array: this.drops,
group: this.particleGroup,
x: x === undefined ? Calc.rand(-this.size / 2, this.size / 2) : x,
y: y === undefined ? Calc.rand(30, 50) : y,
z: z === undefined ? Calc.rand(-this.size / 2, this.size / 2) : z,
size: 0.15,
color: this.dropColor,
opacity: 0,
strength: strength
}, this));
}
updateDrops() {
let i = this.drops.length;
while(i--) {
this.drops[i].update(i);
}
}
createRipple(x, z, strength) {
this.ripples.push(new Ripple({
array: this.ripples,
group: this.particleGroup,
x: x,
y: -0.1,
z: z,
color: this.rippleColor,
strength: strength
}, this));
}
updateRipples() {
let i = this.ripples.length;
while(i--) {
this.ripples[i].update(i);
}
}
update() {
{
let i = this.particles.length;
while(i--) {
this.particles[i].update();
}
}
if(this.tick >= this.dropTick) {
this.createDrop();
this.dropTick = Calc.randInt(this.dropTickMin, this.dropTickMax);
this.tick = 0;
}
this.updateDrops();
this.updateRipples();
{
let i = this.particles.length;
while(i--) {
let j = this.ripples.length;
while(j--) {
let particle = this.particles[i];
let ripple = this.ripples[j];
let influence = ripple.getInfluenceVector(particle.base);
influence.setX(0);
influence.setZ(0);
particle.velocity.add(influence);
}
}
}
this.particleGroup.rotation.x = Math.cos(this.loader.elapsedMilliseconds * 0.0005) * 0.1;
this.particleGroup.rotation.y = Math.PI * 0.25 + Math.sin(this.loader.elapsedMilliseconds * 0.0005) * -0.2;
this.tick += this.loader.deltaTimeNormal;
}
}

View File

@ -0,0 +1,201 @@
/*
------------------------------------------
| rand:float - returns random float
|
| min:number - minimum value
| max:number - maximum value
| ease:function - easing function to apply to the random value
|
| Get a random float between two values,
| with the option of easing bias.
------------------------------------------ */
export function rand(min, max?, ease?) {
if(max === undefined) {
max = min;
min = 0;
}
let random = Math.random();
if(ease) {
random = ease(Math.random(), 0, 1, 1);
}
return random * (max - min) + min;
}
/*
------------------------------------------
| randInt:integer - returns random integer
|
| min:number - minimum value
| max:number - maximum value
| ease:function - easing function to apply to the random value
|
| Get a random integer between two values,
| with the option of easing bias.
------------------------------------------ */
export function randInt(min, max?, ease?) {
if(max === undefined) {
max = min;
min = 0;
}
let random = Math.random();
if(ease) {
random = ease(Math.random(), 0, 1, 1);
}
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/*
------------------------------------------
| randArr:item - returns random iem from array
|
| arr:array - the array to randomly pull from
|
| Get a random item from an array.
------------------------------------------ */
export function randArr(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
/*
------------------------------------------
| map:number - returns a mapped value
|
| val:number - input value
| inputMin:number - minimum of input range
| inputMax:number - maximum of input range
| outputMin:number - minimum of output range
| outputMax:number - maximum of output range
|
| Get a mapped value from and input min/max
| to an output min/max.
------------------------------------------ */
export function map(val, inputMin, inputMax, outputMin, outputMax) {
return ((outputMax - outputMin) * ((val - inputMin) / (inputMax - inputMin))) + outputMin;
}
/*
------------------------------------------
| clamp:number - returns clamped value
|
| val:number - value to be clamped
| min:number - minimum of clamped range
| max:number - maximum of clamped range
|
| Clamp a value to a min/max range.
------------------------------------------ */
export function clamp(val, min, max) {
return Math.max(Math.min(val, max), min);
}
export function lerp(current, target, mix) {
return current + (target - current) * mix;
}
/*
------------------------------------------
| roundToUpperInterval:number - returns rounded up value
|
| value:number - value to be rounded
| interval:number - interval
|
| Round up a value to the next highest interval.
------------------------------------------ */
export function roundToUpperInterval(value, interval) {
if(value % interval === 0) {
value += 0.0001;
}
return Math.ceil(value / interval) * interval;
}
/*
------------------------------------------
| roundDownToInterval:number - returns rounded down value
|
| value:number - value to be rounded
| interval:number - interval
|
| Round down a value to the next lowest interval.
------------------------------------------ */
export function roundToLowerInterval(value, interval) {
if(value % interval === 0) {
value -= 0.0001;
}
return Math.floor(value / interval) * interval;
}
/*
------------------------------------------
| roundToNearestInterval:number - returns rounded value
|
| value:number - value to be rounded
| interval:number - interval
|
| Round a value to the nearest interval.
------------------------------------------ */
export function roundToNearestInterval(value, interval) {
return Math.round(value / interval) * interval;
}
/*
------------------------------------------
| intersectSphere:boolean - returns if intersecting or not
|
| a:object - sphere 1 with radius, x, y, and z
| b:object - sphere 2 with radius, x, y, and z
|
| Check if two sphere are intersecting
| in 3D space.
------------------------------------------ */
export function intersectSphere(a, b) {
let distance = Math.sqrt(
(a.x - b.x) * (a.x - b.x) +
(a.y - b.y) * (a.y - b.y) +
(a.z - b.z) * (a.z - b.z)
);
return distance < (a.radius + b.radius);
}
/*
------------------------------------------
| getIndexFromCoords:number - returns index
|
| x:number - x value (column)
| y:number - y value (row)
| w:number - width of grid
|
| Convert from grid coords to index.
------------------------------------------ */
export function getIndexFromCoords(x, y, w) {
return x + (y * w);
}
/*
------------------------------------------
| getCoordsFromIndex:object - returns coords
|
| i:number - index
| w:number - width of grid
|
| Convert from index to grid coords.
------------------------------------------ */
export function getCoordsFromIndex(i, w) {
return {
x: i % w,
y: Math.floor(i / w)
}
}
export function visibleHeightAtZDepth(depth, camera) {
// https://discourse.threejs.org/t/functions-to-calculate-the-visible-width-height-at-a-given-z-depth-from-a-perspective-camera/269
let cameraOffset = camera.position.z;
if ( depth < cameraOffset ) depth -= cameraOffset;
else depth += cameraOffset;
let vFOV = camera.fov * Math.PI / 180;
return 2 * Math.tan( vFOV / 2 ) * Math.abs( depth );
}
export function visibleWidthAtZDepth(depth, camera) {
// https://discourse.threejs.org/t/functions-to-calculate-the-visible-width-height-at-a-given-z-depth-from-a-perspective-camera/269
let height = visibleHeightAtZDepth( depth, camera );
return height * camera.aspect;
}

View File

@ -0,0 +1,433 @@
/*
------------------------------------------
| inQuad:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inQuad.
------------------------------------------ */
export function inQuad(t, b, c, d) {
return c*(t/=d)*t + b;
}
/*
------------------------------------------
| outQuad:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on outQuad.
------------------------------------------ */
export function outQuad(t, b, c, d) {
return -c *(t/=d)*(t-2) + b;
}
/*
------------------------------------------
| inOutQuad:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inOutQuad.
------------------------------------------ */
export function inOutQuad(t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
}
/*
------------------------------------------
| inCubic:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inCubic.
------------------------------------------ */
export function inCubic(t, b, c, d) {
return c*(t/=d)*t*t + b;
}
/*
------------------------------------------
| outCubic:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on outCubic.
------------------------------------------ */
export function outCubic(t, b, c, d) {
return c*((t=t/d-1)*t*t + 1) + b;
}
/*
------------------------------------------
| inOutCubic:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inOutCubic.
------------------------------------------ */
export function inOutCubic(t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
}
/*
------------------------------------------
| inQuart:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inQuart.
------------------------------------------ */
export function inQuart(t, b, c, d) {
return c*(t/=d)*t*t*t + b;
}
/*
------------------------------------------
| outQuart:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on outQuart.
------------------------------------------ */
export function outQuart(t, b, c, d) {
return -c * ((t=t/d-1)*t*t*t - 1) + b;
}
/*
------------------------------------------
| inOutQuart:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inOutQuart.
------------------------------------------ */
export function inOutQuart(t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
return -c/2 * ((t-=2)*t*t*t - 2) + b;
}
/*
------------------------------------------
| inQuint:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inQuint.
------------------------------------------ */
export function inQuint(t, b, c, d) {
return c*(t/=d)*t*t*t*t + b;
}
/*
------------------------------------------
| outQuint:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on outQuint.
------------------------------------------ */
export function outQuint(t, b, c, d) {
return c*((t=t/d-1)*t*t*t*t + 1) + b;
}
/*
------------------------------------------
| inOutQuint:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inOutQuint.
------------------------------------------ */
export function inOutQuint(t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
return c/2*((t-=2)*t*t*t*t + 2) + b;
}
/*
------------------------------------------
| inSine:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inSine.
------------------------------------------ */
export function inSine(t, b, c, d) {
return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
}
/*
------------------------------------------
| outSine:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on outSine.
------------------------------------------ */
export function outSine(t, b, c, d) {
return c * Math.sin(t/d * (Math.PI/2)) + b;
}
/*
------------------------------------------
| inOutSine:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inOutSine.
------------------------------------------ */
export function inOutSine(t, b, c, d) {
return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
}
/*
------------------------------------------
| inExpo:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inExpo.
------------------------------------------ */
export function inExpo(t, b, c, d) {
return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
}
/*
------------------------------------------
| outExpo:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on outExpo.
------------------------------------------ */
export function outExpo(t, b, c, d) {
return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
}
/*
------------------------------------------
| inOutExpo:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inOutExpo.
------------------------------------------ */
export function inOutExpo(t, b, c, d) {
if (t==0) return b;
if (t==d) return b+c;
if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
}
/*
------------------------------------------
| inCirc:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inCirc.
------------------------------------------ */
export function inCirc(t, b, c, d) {
return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
}
/*
------------------------------------------
| outCirc:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on outCirc.
------------------------------------------ */
export function outCirc(t, b, c, d) {
return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
}
/*
------------------------------------------
| inOutCirc:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inOutCirc.
------------------------------------------ */
export function inOutCirc(t, b, c, d) {
if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
}
/*
------------------------------------------
| inElastic:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inElastic.
------------------------------------------ */
export function inElastic(t, b, c, d) {
let s=1.70158;let p=0;let a=c;
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (a < Math.abs(c)) { a=c; let s=p/4; }
else s = p/(2*Math.PI) * Math.asin (c/a);
return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
}
/*
------------------------------------------
| outElastic:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on outElastic.
------------------------------------------ */
export function outElastic(t, b, c, d) {
let s=1.70158;let p=0;let a=c;
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (a < Math.abs(c)) { a=c; let s=p/4; }
else s = p/(2*Math.PI) * Math.asin (c/a);
return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
}
/*
------------------------------------------
| inOutElastic:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
|
| Get an eased float value based on inOutElastic.
------------------------------------------ */
export function inOutElastic(t, b, c, d) {
let s=1.70158;let p=0;let a=c;
if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
if (a < Math.abs(c)) { a=c; let s=p/4; }
else s = p/(2*Math.PI) * Math.asin (c/a);
if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
}
/*
------------------------------------------
| inBack:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
| s:number - strength
|
| Get an eased float value based on inBack.
------------------------------------------ */
export function inBack(t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c*(t/=d)*t*((s+1)*t - s) + b;
}
/*
------------------------------------------
| outBack:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
| s:number - strength
|
| Get an eased float value based on outBack.
------------------------------------------ */
export function outBack(t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
}
/*
------------------------------------------
| inOutBack:float - returns eased float value
|
| t:number - current time
| b:number - beginning value
| c:number - change in value
| d:number - duration
| s:number - strength
|
| Get an eased float value based on inOutBack.
------------------------------------------ */
export function inOutBack(t, b, c, d, s) {
if (s == undefined) s = 1.70158;
if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
}

View File

@ -0,0 +1,7 @@
export function scrollTo(qs: string) {
if (process.client) {
document.querySelector(qs)?.scrollIntoView({
behavior: 'smooth',
});
}
}

View File

@ -0,0 +1 @@
<svg class="bi" fill="currentColor" version="1.1" viewBox="0 0 135.47 135.47" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-38.1 -100.7)"><path d="m54.63 120.96c-1.9779 0-3.8618 0.32979-5.6513 0.98909-3.2023 1.1302-5.8396 3.1553-7.9117 6.0751-1.9779 2.8256-2.9667 5.9807-2.9667 9.4656v61.88c0 4.5209 1.601 8.4294 4.8033 11.726 3.2965 3.2023 7.2055 4.8038 11.726 4.8038 4.6151 0 8.5236-1.6015 11.726-4.8038 3.2965-3.2965 4.9449-7.205 4.9449-11.726v-11.253c0.03556-2.4371 2.546-1.7977 3.8148 0 2.3763 4.1153 7.4142 7.6497 13.28 7.6295 5.8656-0.0202 10.737-2.9202 13.28-7.6295 0.96304-1.1358 3.6774-3.071 3.9558 0v11.253c0 4.5209 1.601 8.4294 4.8033 11.726 3.2965 3.2023 7.2055 4.8038 11.726 4.8038 4.6151 0 8.5236-1.6015 11.726-4.8038 3.2965-3.2965 4.9449-7.205 4.9449-11.726v-61.88c0-3.4849-1.0357-6.64-3.1078-9.4656-1.9779-2.9198-4.5683-4.9448-7.7706-6.0751-1.8837-0.6593-3.7676-0.98909-5.6513-0.98909-5.086 0-9.3712 1.9782-12.856 5.934l-16.775 19.632c-0.37674 0.28256-1.6248 2.4428-4.2757 2.4428-2.6509 0-3.7574-2.1602-4.1341-2.4428l-16.916-19.632c-3.3907-3.9558-7.6289-5.934-12.715-5.934zm104.53 0c-3.9558 0-7.3464 1.4129-10.172 4.2385-2.7314 2.7314-4.0969 6.0751-4.0969 10.031 0 3.9558 1.3655 7.3464 4.0969 10.172 2.8256 2.7314 6.2162 4.0974 10.172 4.0974 3.9558 0 7.3464-1.366 10.172-4.0974 2.8256-2.8256 4.2385-6.2162 4.2385-10.172 0-3.9558-1.4129-7.2995-4.2385-10.031-2.8256-2.8256-6.2162-4.2385-10.172-4.2385zm0.14107 31.364c-3.9558 0-7.3464 1.4129-10.172 4.2385s-4.238 6.2162-4.238 10.172v34.896c0 3.9558 1.4124 7.3464 4.238 10.172 2.8256 2.7314 6.2162 4.0974 10.172 4.0974s7.2995-1.366 10.031-4.0974c2.8256-2.8256 4.2385-6.2162 4.2385-10.172v-34.896c0-3.9558-1.4129-7.3464-4.2385-10.172-2.7314-2.8256-6.0751-4.2385-10.031-4.2385z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

56
components/g/Button.vue Normal file
View File

@ -0,0 +1,56 @@
<template>
<GNuxtLink v-if="buttonType == 'link'" :to="to" :class="[$style.button, color == 'accent' ? $style.buttonAccent : $style.buttonPlain]">
<slot></slot>
</GNuxtLink>
<button v-else-if="buttonType == 'button'" @click="onClick()" :class="[$style.button, color == 'accent' ? $style.buttonAccent : $style.buttonPlain]">
<slot></slot>
</button>
</template>
<script setup lang="ts">
const props = withDefaults(defineProps<({
/** ボタンのタイプ */
buttonType: 'button';
} | {
/** ボタンのタイプ */
buttonType: 'link';
/** 移動先 */
to: string;
}) & {
/** 色設定 */
color: 'accent' | 'plain';
}>(), {
color: 'plain',
});
const emits = defineEmits<{
(e: 'click'): void;
}>();
function onClick() {
emits('click');
}
</script>
<style module>
.button {
@apply py-3 px-6 text-lg font-bold rounded-full transition-all;
box-shadow: 0 8px 20px -5px rgba(0, 0, 0, .2);
}
.buttonAccent {
@apply text-white;
box-shadow: 0 8px 20px -5px rgba(134, 179, 0, .4);
background-image: linear-gradient(90deg, #86b300, #4ab300, #4ab300);
background-size: 200% 100%;
background-position-x: 0%;
}
.buttonAccent:hover {
background-position-x: 100%;
}
.buttonPlain {
@apply text-accent-600 bg-white hover:text-accent-700 hover:bg-lime-100;
}
</style>

21
components/g/Dots.vue Normal file
View File

@ -0,0 +1,21 @@
<template>
<svg fill="none">
<defs>
<pattern :id="id" x="0" y="0" :width="space" :height="space" patternUnits="userSpaceOnUse">
<circle cx="2" cy="2" r="2" fill="currentColor"></circle>
</pattern>
</defs>
<rect width="100%" height="100%" :fill="`url(#${id})`"></rect>
</svg>
</template>
<script setup lang="ts">
withDefaults(defineProps<{
space?: number;
}>(), {
space: 15,
});
const idchars = 'abcdefghijklmnopqrstuvwxyz';
const id = Array.from(Array(32)).map(() => idchars[Math.floor(Math.random() * idchars.length)]).join('');
</script>

View File

@ -1,11 +1,13 @@
<template>
<div>
<nav class="h-[4rem] fixed top-0 left-0 w-full bg-white dark:bg-gray-700 bg-opacity-80 backdrop-blur-lg z-[9950]">
</div>
</nav>
</template>
<script setup lang="ts">
defineProps<{
isOpen: boolean;
}>();
</script>
<style scoped>

6
components/g/NuxtLink.ts Normal file
View File

@ -0,0 +1,6 @@
// 末尾スラッシュの統一のためにラッパーNuxtLinkを実装
export default defineNuxtLink({
componentName: "NuxtLink",
trailingSlash: 'append',
});

View File

@ -0,0 +1,20 @@
<template>
<div class="bg-white dark:bg-slate-950 rounded-2xl p-6 lg:p-12 lg:py-24">
<I18nT keypath="_landing._decenterized.title" tag="h2" class="text-center text-xl lg:text-3xl font-bold font-title mb-6">
<span class="em">{{ $t('_landing._decenterized.decenterizedPlatform') }}</span>
</I18nT>
<I18nT keypath="_landing._decenterized.description" tag="p" class="lg:text-lg leading-relaxed lg:leading-loose">
<span class="font-bold em">{{ $t('_landing._decenterized.activityPub') }}</span>
</I18nT>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
.em {
background: linear-gradient(transparent 70%,rgba(191,255,0,.5019607843) 0%);
}
</style>

View File

@ -0,0 +1,12 @@
<template>
<div class="bg-white dark:bg-slate-950 rounded-2xl p-6 lg:p-12 lg:py-24 mx-auto max-w-screen-lg">
<h2 class="text-center text-xl lg:text-3xl font-bold font-title mb-6">{{ $t('_landing._donation.title') }}</h2>
<p class="lg:text-lg leading-relaxed lg:leading-loose mb-6">{{ $t('_landing._donation.description') }}</p>
<p class="text-center">
<GButton button-type="link" :to="localePath('/docs/donate/')" color="accent">{{ $t('learnMore') }}</GButton>
</p>
</div>
</template>
<script setup lang="ts">
const localePath = useLocalePath();
</script>

View File

@ -0,0 +1,93 @@
<template>
<div class="grid features gap-12">
<div>
<div class="sticky top-12">
<h2 class="font-title font-bold text-2xl lg:text-4xl mb-6">{{ $t('_landing._features._root.title') }}</h2>
<p class="text-lg">{{ $t('_landing._features._root.description') }}</p>
</div>
</div>
<div class="space-y-8">
<div v-fade-in class="item"><div class="content">
<img src="/img/top-features/top-features-note.png" class="img" alt="some notes">
<h3 class="title">{{ $t('_landing._features._note.title') }}</h3>
<div class="description">{{ $t('_landing._features._note.description') }}</div>
</div></div>
<div v-fade-in class="item"><div class="content">
<img src="/img/top-features/top-features-reaction.png" class="img" alt="some emojis in the reaction picker">
<h3 class="title">{{ $t('_landing._features._reaction.title') }}</h3>
<div class="description">{{ $t('_landing._features._reaction.description') }}</div>
</div></div>
<div v-fade-in class="item"><div class="content">
<img src="/img/top-features/top-features-theme.png" class="img" alt="color palette">
<h3 class="title">{{ $t('_landing._features._theme.title') }}</h3>
<div class="description">{{ $t('_landing._features._theme.description') }}</div>
</div></div>
<div v-fade-in class="item"><div class="content">
<img src="/img/top-features/top-features-charts.png" class="img" alt="Charts">
<h3 class="title">{{ $t('_landing._features._charts.title') }}</h3>
<div class="description">{{ $t('_landing._features._charts.description') }}</div>
</div></div>
</div>
<div class="space-y-8 lg:pt-24">
<div v-fade-in class="item"><div class="content">
<img src="/img/top-features/top-features-federation.png" class="img" alt="logos of Misskey and other ActivityPub server software">
<h3 class="title">{{ $t('_landing._features._federation.title') }}</h3>
<div class="description">{{ $t('_landing._features._federation.description') }}</div>
</div></div>
<div v-fade-in class="item"><div class="content">
<img src="/img/top-features/top-features-drive.png" class="img" alt="a list of files in Misskey Drive">
<h3 class="title">{{ $t('_landing._features._drive.title') }}</h3>
<div class="description">{{ $t('_landing._features._drive.description') }}</div>
</div></div>
<div v-fade-in class="item"><div class="content">
<img src="/img/top-features/top-features-thread.png" class="img" alt="multiple messages arranged in a thread">
<h3 class="title">{{ $t('_landing._features._thread.title') }}</h3>
<div class="description">{{ $t('_landing._features._thread.description') }}</div>
</div></div>
<div v-fade-in class="item"><div class="content">
<img src="/img/top-features/top-features-widgets.png" class="img" alt="sample widget showing activity as colored dots">
<h3 class="title">{{ $t('_landing._features._widgets.title') }}</h3>
<div class="description">{{ $t('_landing._features._widgets.description') }}</div>
</div></div>
</div>
</div>
</template>
<script setup lang="ts">
import { vFadeIn } from '@/assets/js/fadein';
</script>
<style scoped>
.features {
grid-template-columns: 1fr;
}
@screen lg {
.features {
grid-template-columns: 300px repeat(2, 1fr);
}
}
.item > .content {
@apply p-5 bg-white dark:bg-slate-700 rounded-xl;
}
.item .img {
@apply w-full aspect-[2/1] object-cover rounded-lg mb-4;
}
.item .title {
@apply font-title text-xl lg:text-2xl font-bold mb-2;
}
.__v_fadeIn_out > * {
opacity: 0;
transform: scale(0.9) translateY(-50px);
}
.__v_fadeIn_in > * {
opacity: 1;
transform: scale(1) translateY(0);
}
</style>

View File

@ -0,0 +1,63 @@
<template>
<div>
<h2 class="mb-12 text-2xl lg:text-3xl text-center font-bold font-title">{{ $t('_landing._getStarted.title') }}</h2>
<div class="grid gap-12 lg:grid-cols-3">
<div v-fade-in class="find">
<div class="item">
<div class="icon"><img src="/img/emojis/ringed-planet_1fa90.png" aria-hidden="true"></div>
<h3 class="title">{{ $t('_landing._getStarted._find.title') }}</h3>
<div class="description"></div>
<GNuxtLink class="link" :to="localePath('/servers/')">{{ $t('_landing._getStarted._find.list') }}</GNuxtLink>
</div>
</div>
<div v-fade-in class="create">
<div class="item">
<div class="icon"><img src="/img/emojis/package_1f4e6.png" aria-hidden="true"></div>
<h3 class="title">{{ $t('_landing._getStarted._create.title') }}</h3>
<div class="description"></div>
<GNuxtLink class="link" to="./docs/install.html">{{ $t('_landing._getStarted._create.guide') }}</GNuxtLink>
</div>
</div>
<div v-fade-in class="docs">
<div class="item">
<div class="icon"><img src="/img/emojis/light-bulb_1f4a1.png" aria-hidden="true"></div>
<h3 class="title">{{ $t('_landing._getStarted._docs.title') }}</h3>
<div class="description"></div>
<GNuxtLink class="link" to="./docs/misskey.html">{{ $t('_landing._getStarted._docs.docs') }}</GNuxtLink>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { vFadeIn } from 'assets/js/fadein';
const localePath = useLocalePath();
</script>
<style scoped>
.item {
@apply rounded-2xl py-12 px-4 text-center bg-white dark:bg-slate-700;
}
.find > .item {
@apply bg-accent-600 bg-opacity-60 backdrop-blur-lg text-white;
}
.item > .icon {
@apply w-12 mb-4 mx-auto;
}
.item > .title {
@apply font-title font-bold text-xl text-center mb-6;
}
.item > .link {
@apply px-6 py-3 rounded-lg bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors font-bold text-lg;
}
.find .link {
@apply text-accent-600 bg-white hover:bg-accent-100;
}
</style>

View File

@ -1,23 +0,0 @@
<template>
<div class="container mx-auto">
<MkLogo />
<div class="tagline" id="tagline">
<div class="row">Interplanetary</div>
<div class="row">microblogging</div>
<div class="row">platform.🚀</div>
</div>
<div class="description">{{ $t('_landing._hero.description') }}</div>
<div class="buttons">
<a class="start" href="#gettingStarted">{{ $t('_landing._hero.gettingStarted') }}</a>
<a class="more" href="#learnMore">{{ $t('_landing._hero.learnMore') }}</a>
</div>
</div>
</template>
<script setup lang="ts">
import MkLogo from '@/assets/svg/misskey-logotype.svg';
</script>
<style scoped>
</style>

View File

@ -0,0 +1,47 @@
<template>
<div class="backdrop-blur-lg bg-white dark:bg-slate-900 bg-opacity-50 dark:bg-opacity-50 rounded-2xl p-6 lg:p-12 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div class="item">
<div class="icon"><img src="/img/emojis/four-leaf-clover_1f340.png" aria-hidden="true"></div>
<h3 class="title">{{ $t('_landing._keyFeatures._open.title') }}</h3>
<div class="description">{{ $t('_landing._keyFeatures._open.description') }}</div>
</div>
<div class="item">
<div class="icon"><img src="/img/emojis/ringed-planet_1fa90.png" aria-hidden="true"></div>
<h3 class="title">{{ $t('_landing._keyFeatures._federated.title') }}</h3>
<div class="description">{{ $t('_landing._keyFeatures._federated.description') }}</div>
</div>
<div class="item">
<div class="icon"><img src="/img/emojis/package_1f4e6.png" aria-hidden="true"></div>
<h3 class="title">{{ $t('_landing._keyFeatures._multifunction.title') }}</h3>
<div class="description">{{ $t('_landing._keyFeatures._multifunction.description') }}</div>
</div>
<div class="item">
<div class="icon"><img src="/img/emojis/gear_2699-fe0f.png" aria-hidden="true"></div>
<h3 class="title">{{ $t('_landing._keyFeatures._customizable.title') }}</h3>
<div class="description">{{ $t('_landing._keyFeatures._customizable.description') }}</div>
</div>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
.item {
@apply space-y-2;
}
.title {
@apply font-title font-bold text-center text-xl md:text-2xl;
}
.icon {
text-align: center;
}
.icon img {
width: 70px;
margin: 0 auto;
}
</style>

56
components/index/Nav.vue Normal file
View File

@ -0,0 +1,56 @@
<template>
<nav class="sticky top-0 z-[9900] md:relative container mx-auto max-w-screen-xl h-16 lg:h-20 grid items-center grid-cols-2 md:grid-cols-4 lg:grid-cols-6 p-4 bg-white bg-opacity-80 lg:bg-transparent">
<div class="">
<GNuxtLink to="/" class="flex items-center space-x-2 hover:opacity-80">
<MiIcon class="h-8 w-8" />
<div class="font-title font-bold text-lg">Misskey Hub</div>
</GNuxtLink>
</div>
<ul class="hidden lg:col-span-4 lg:space-x-8 xl:space-x-10 lg:flex justify-center">
<li v-for="item in NavData.center">
<GNuxtLink :to="localePath(item.to)" class="rounded-full px-4 py-1.5 hover:bg-slate-300 hover:bg-opacity-50 bg-blend-multiply">
<component v-if="item.icon" :is="item.icon" class="h-5 w-5" />
<template v-else>
{{ $t(item.i18n) }}
</template>
</GNuxtLink>
</li>
</ul>
<div>
<ul class="hidden lg:col-span-4 lg:space-x-4 lg:flex justify-center">
<li class="relative group">
<a class="text-white hover:opacity-80" href="#"><I18nIcon class="h-5 w-5" /><span class="sr-only">{{ $t('_nav.switchLang') }}</span></a>
<div class="absolute top-6 right-0 hidden group-hover:block z-[9955]">
<ul class="px-4 py-2 bg-slate-50 dark:bg-slate-700 rounded-lg shadow-lg space-y-1">
<li v-for="locale in locales">
<GNuxtLink :to="switchLocalePath(locale.code)" class="hover:text-accent-600">
{{ locale.name }}
</GNuxtLink>
</li>
</ul>
</div>
</li>
<li class="border-l"></li>
<li v-for="item in NavData.right" class="text-white">
<GNuxtLink :to="item.to" class="hover:opacity-80">
<component v-if="item.icon" :is="item.icon" class="h-5 w-5" />
<template v-else>
{{ $t(item.i18n) }}
</template>
</GNuxtLink>
</li>
</ul>
</div>
</nav>
</template>
<script setup>
import MiIcon from '@/assets/svg/misskey_mi_bi.svg';
import I18nIcon from 'bi/translate.svg';
import NavData from '@/assets/data/nav';
const { locales } = useI18n();
const switchLocalePath = useSwitchLocalePath();
const localePath = useLocalePath();
</script>

View File

@ -1,5 +1,5 @@
<template>
<div class="absolute top-0 left-0 w-full h-full overflow-hidden">
<div class="absolute z-0 top-0 left-0 w-full h-full overflow-hidden">
<div v-parallax="1.2" class="blobs object1"><Blob1 aria-hidden="true" /></div>
<div v-parallax="1.2" class="blobs object2"><Blob2 aria-hidden="true" /></div>
<div v-parallax="1.2" class="blobs object3"><Blob2 aria-hidden="true" /></div>
@ -7,20 +7,11 @@
</template>
<script setup lang="ts">
import type { Ref } from 'vue';
import { vParallax } from '@/assets/js/parallax';
import Blob1 from '@/assets/svg/top-bg-object1.svg';
import Blob2 from '@/assets/svg/top-bg-object2.svg';
const vParallax = {
mounted: (src: HTMLElement, binding: Ref<number>) => {
src.style.willChange = 'transform';
window.addEventListener('scroll', () => {
src.style.transform = `translateY(${window.scrollY / binding.value}px)`;
}, { passive: true });
}
}
</script>
<style scoped>
@ -46,6 +37,7 @@ const vParallax = {
left: -250px;
top: 500px;
width: 1000px;
opacity: .5;
}
.object2 > svg {
@ -56,6 +48,7 @@ const vParallax = {
right: -300px;
top: 1400px;
width: 1000px;
opacity: .5;
}
.object3 > svg {
@ -67,3 +60,4 @@ const vParallax = {
100% { transform: rotate(360deg); }
}
</style>
assets/js/paralax

View File

@ -0,0 +1,42 @@
<template>
<div class="relative space-y-6">
<MkLogo class="block mx-auto lg:ml-0 w-full max-w-[250px]" />
<h2 ref="tagline" class="text-center font-title lg:text-start font-bold tracking-wide text-3xl sm:text-5xl md:text-6xl leading-relaxed sm:leading-relaxed md:leading-relaxed" id="tagline">
<div class="row">Interplanetary</div>
<div class="row">microblogging</div>
<div class="row">platform.🚀</div>
</h2>
<div class="max-w-lg mx-auto lg:mx-0 text-lg text-center lg:text-start">{{ $t('_landing._hero.description') }}</div>
<div class="w-fit space-x-4 mx-auto lg:mx-0">
<GButton button-type="button" color="accent" @click="scrollTo('#getStarted')">{{ $t('_landing._hero.gettingStarted') }}</GButton>
<GButton button-type="button" @click="scrollTo('#learnMore')">{{ $t('learnMore') }}</GButton>
</div>
</div>
</template>
<script setup lang="ts">
import MkLogo from '@/assets/svg/misskey-logotype.svg';
import { scrollTo } from '@/assets/js/scroll-to';
const tagline = ref<HTMLElement>();
onMounted(() => {
window.setTimeout(() => {
if (tagline.value) {
for (let i = 0; i < tagline.value.children.length; i++) {
const row = tagline.value.children[i];
window.setTimeout(() => { row.classList.add('shown'); }, 200 * i);
}
}
}, 250);
});
</script>
<style scoped>
.row {
@apply opacity-0 translate-x-24 transition duration-1000
}
.row.shown {
@apply opacity-100 translate-x-0;
}
</style>

View File

@ -0,0 +1,61 @@
<template>
<ClientOnly>
<transition name="fade">
<div v-if="particleEnabled" ref="container" class="absolute top-0 left-0 w-full h-[800px] select-none pointer-events-none" :style="colorVars"></div>
</transition>
</ClientOnly>
</template>
<script setup lang="ts">
import { Loader } from '@/assets/js/particles/loader';
import tailwindConfig from 'tailwindcss/resolveConfig';
const colorMode = useColorMode();
const container = ref<HTMLElement>();
const isMounted = ref<boolean>(false);
const particleEnabled = ref<boolean>(true);
const colorVars = computed<string>(() => {
const out = [`--c-brand: ${tailwindConfig.theme?.extend.colors.accent['600'] || '#86b300'}`]
if (colorMode.preference == 'dark') {
out.join(`--c-text: `)
}
return out.join(' ');
});
onMounted(() => {
isMounted.value = true;
});
watch(container, (to) => {
if (isMounted.value && process.client && to) {
const loader = new Loader(to);
window.addEventListener('scroll', () => {
particleEnabled.value = false;
}, {
passive: true,
once: true,
});
onUnmounted(() => {
setTimeout(() => {
loader.destroy();
})
})
}
})
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 1s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>

View File

@ -0,0 +1,105 @@
<template>
<div class="absolute top-0 w-full hidden lg:block">
<GDots v-parallax="1.4" class="dots dots1" :space="30"/>
<GDots v-parallax="1.5" class="dots dots2" :space="30"/>
<img v-parallax="2" src="/img/hero/screenshot-desktop.png" class="screenshot desktop" alt="screenshot of Misskey in a PC browser">
<img v-parallax="3" src="/img/hero/screenshot-mobile.png" class="screenshot mobile" alt="screenshot of Misskey in a mobile browser">
<img v-parallax="4" src="/img/hero/ai.png" class="ai" alt="Ai-chan, Misskey's mascott">
</div>
</template>
<script setup lang="ts">
import { vParallax } from '@/assets/js/parallax';
</script>
<style scoped>
.dots {
@apply absolute text-accent-600 pointer-events-none select-none w-[300px] h-[300px];
}
.dots1 {
right: 900px;
top: 200px;
}
.dots2 {
right: 120px;
top: 500px;
}
.screenshot {
@apply absolute rounded-lg shadow-lg select-none pointer-events-none;
}
.screenshot.mobile {
right: 650px;
top: 400px;
height: 400px;
}
.screenshot.desktop {
width: 800px;
top: 128px;
right: 300px;
}
.ai {
@apply absolute select-none pointer-events-none;
right: 130px;
top: 128px;
height: 900px;
}
@media (max-width: 1800px) {
.dots1 {
right:800px
}
.screenshot.desktop {
width: 700px
}
}
@media (max-width: 1700px) {
.dots1 {
right:700px
}
.screenshot.desktop {
width: 600px
}
.screenshot.mobile {
height: 350px;
right: 500px
}
}
@media (max-width: 1600px) {
.dots1 {
right:600px
}
.screenshot.desktop {
width: 500px
}
.screenshot.mobile {
height: 300px
}
}
@media (max-width: 1500px) {
.dots1 {
right:600px
}
.screenshot.desktop {
right: 250px
}
.screenshot.mobile {
right: 450px
}
}
</style>

View File

@ -4,7 +4,7 @@ const isNavOpen = ref<boolean>(false);
<template>
<div>
<GNav @toggleNav="isNavOpen = !isNavOpen" :is-open="isNavOpen" />
<div class="min-h-screen main-content overflow-x-hidden">
<div class="main-content overflow-x-hidden">
<slot></slot>
</div>
<GFooter />

11
layouts/landing.vue Normal file
View File

@ -0,0 +1,11 @@
<script setup lang="ts">
const isNavOpen = ref<boolean>(false);
</script>
<template>
<div>
<div class="main-content">
<slot></slot>
</div>
<GFooter />
</div>
</template>

View File

@ -1,10 +1,75 @@
noScript: "現在Javascriptが無効になっています。サイトの表示にはJavascriptが必須となりますので有効にしてください。"
learnMore: "詳しく知る"
_seo:
siteName: "Misskey Hub"
defaultTitleTagline: "ノートでひろがるネットワーク"
defaultDescription: "Misskeyはオープンソースの分散型ソーシャルネットワーキングプラットフォームです。"
_nav:
servers: "サーバー"
docs: "ドキュメント"
blog: "ブログ"
switchLang: "言語の設定"
_landing:
_hero:
description: "Misskeyはオープンソースの分散型ソーシャルネットワーキングプラットフォームです。"
gettingStarted: "はじめる"
learnMore: "詳しく知る"
_keyFeatures:
_open:
title: "オープン"
description: "Misskeyはオープンソースのソフトウェアで、誰でも自由にMisskeyを利用できます。"
_federated:
title: "分散"
description: "Misskeyは分散型のプロトコルを実装しているため、異なるサーバーのコミュニティ同士が繋がります。"
_multifunction:
title: "高機能"
description: "Misskeyは他にはない様々な機能を備えていて、プロのようにmicro-blogging可能です。"
_customizable:
title: "高カスタマイズ性"
description: "Misskeyの柔軟なWebインターフェイスにより、自分好みのUIを作れます。"
_decenterized:
title: "Misskeyは{0}"
decenterizedPlatform: "分散型プラットフォーム"
description: "Misskeyはフリーかつオープンなプロジェクトで、誰でも自由にMisskeyを使ったサーバーを作成できるため、既に様々なサーバーがインターネット上に公開されています。また重要な特徴として、Misskeyは{0}と呼ばれる分散通信プロトコルを実装しているので、どのサーバーを選んでも他のサーバーのユーザーとやりとりすることができます。これが分散型と言われる所以で、単一の運営者によって単一のURLで公開されるような、Twitterなどの他サービスとは根本的に異なっています。サーバーによって主な話題のテーマやユーザー層、言語などは異なり、自分にあったサーバーを探すのも楽しみのひとつです(もちろん自分のサーバーを作るのも一興です)。"
activityPub: "ActivityPub"
_features:
_root:
title: "主な機能"
description: "Misskeyは一般的なものから特別なものまで、様々な機能を持っています。その一部を紹介します。"
_note:
title: "ノート"
description: "Misskeyでは、ユーザーの投稿は「ート」と呼ばれます。他のートを引用したり、画像、動画、オーディオ、その他の任意のファイルを添付することもできます。"
_federation:
title: "連合"
description: "オープンな分散プロトコルであるActivityPubを実装しているため、他のMisskeyサーバーだけでなく、ActivityPubをサポートする他のソフトウェアともやりとりできます。"
_reaction:
title: "リアクション"
description: "ノートには「リアクション」を付けることができ、簡単・気軽に自分のフィーリングを表現して伝えることが出来ます。"
_drive:
title: "ドライブ"
description: "アップロードしたファイルを管理するインターフェイスがあります。そのため、お気に入りの画像をフォルダにまとめたり、再度共有することも簡単に行えます。"
_theme:
title: "テーマ"
description: "自分の好きなデザインでMisskeyを使えます。もちろんダークモードも完全サポート。自分で高度にテーマを作ることも可能です。"
_thread:
title: "スレッド"
description: "もちろんノートはスレッドにすることができ、気が済むまで会話を続けられます。"
_charts:
title: "チャート"
description: "Misskeyは組み込みのチャートエンジンを備えていて、サーバーの利用状況などが簡単に可視化できます。"
_widgets:
title: "ウィジェット"
description: "様々な種類のウィジェットを配置し、UIを好みにカスタマイズできます。"
_getStarted:
title: "Misskeyをはじめよう"
_find:
title: "サーバーを見つける"
list: "サーバーのリスト"
_create:
title: "サーバーを作成"
guide: "セットアップガイド"
_docs:
title: "さらに詳しく知る"
docs: "ドキュメントを見る"
_donation:
title: "寄付のお願い"
description: "Misskeyは非営利なため、開発資金は皆様からの寄付に頼っています。Misskeyを気に入られたら、今後も開発を続けられるようにぜひ支援をお願いします。"

View File

@ -11,13 +11,14 @@ export default defineNuxtConfig({
modules: [
'@nuxt/content',
'@nuxtjs/i18n',
'@nuxtjs/color-mode',
],
app: {
head: {
link: [
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
{ rel: 'preconnect', href: 'https://fonts.gstatic.com' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Nunito:wght@400;700&display=swap' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Capriola&family=Nunito:ital,wght@0,400;0,700;1,400;1,700&display=swap' },
{ rel: 'stylesheet', href: '/fonts/fonts.css' },
],
},
@ -25,12 +26,19 @@ export default defineNuxtConfig({
i18n: {
vueI18n: './i18n.config.ts',
locales: [
{ code: 'ja', iso: 'ja-JP' },
{ code: 'en', iso: 'en-US' },
{ code: 'ja', iso: 'ja-JP', name: '日本語' },
{ code: 'en', iso: 'en-US', name: 'English' },
{ code: 'ko', iso: 'ko-KR', name: '한국어' },
{ code: 'it', iso: 'it-IT', name: 'Italiano' },
{ code: 'pl', iso: 'pl-PL', name: 'Polski' },
{ code: 'fr', iso: 'fr-FR', name: 'Français' }
],
defaultLocale: 'ja',
strategy: 'prefix',
},
colorMode: {
classSuffix: '',
},
postcss: {
plugins: {
tailwindcss: {},

View File

@ -12,16 +12,22 @@
"@modyfi/vite-plugin-yaml": "^1.0.4",
"@nuxt/content": "^2.7.0",
"@nuxt/devtools": "latest",
"@nuxtjs/color-mode": "^3.3.0",
"@nuxtjs/i18n": "8.0.0-beta.13",
"@types/node": "^18",
"@types/three": "^0.153.0",
"@types/tinycolor2": "^1.4.3",
"autoprefixer": "^10.4.14",
"bootstrap-icons": "^1.10.5",
"github-markdown-css": "^5.2.0",
"meshline": "^3.1.6",
"nuxt": "^3.6.2",
"postcss": "^8.4.25",
"schema-dts": "^1.1.2",
"sitemap": "^7.1.1",
"tailwindcss": "^3.3.2",
"three": "^0.154.0",
"tinycolor2": "^1.6.0",
"vite-svg-loader": "^4.0.0"
},
"packageManager": "pnpm@8.6.0"

View File

@ -1,12 +1,29 @@
<template>
<div class="relative bg-gray-50 min-h-screen">
<IndexBg />
<IndexHero />
<div class="relative min-h-full">
<IndexHeroBg />
<IndexHeroParticles />
<IndexNav />
<IndexHeroRight />
<div class="relative container mx-auto p-6 md:p-8 max-w-screen-sm lg:max-w-screen-xl">
<IndexHeroLeft />
</div>
<main class="relative container mx-auto max-w-screen-xl px-6 mt-32 space-y-16">
<IndexKeyFeatures id="learnMore" />
<IndexDecenterized />
<GDots class="w-[95%] mx-auto text-accent-600" :space="30" />
<IndexFeatures />
<GDots class="w-[95%] mx-auto text-accent-600" :space="30" />
<IndexGetStarted id="getStarted" />
<GDots class="w-[95%] mx-auto text-accent-600" :space="30" />
<IndexDonation />
</main>
</div>
</template>
<script setup lang="ts">
definePageMeta({
layout: 'landing',
});
</script>
<style scoped>

13
pages/servers.vue Normal file
View File

@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>

View File

@ -14,12 +14,21 @@ devDependencies:
'@nuxt/devtools':
specifier: latest
version: 0.6.7(nuxt@3.6.2)(vite@4.4.2)
'@nuxtjs/color-mode':
specifier: ^3.3.0
version: 3.3.0
'@nuxtjs/i18n':
specifier: 8.0.0-beta.13
version: 8.0.0-beta.13(vue@3.3.4)
'@types/node':
specifier: ^18
version: 18.0.0
'@types/three':
specifier: ^0.153.0
version: 0.153.0
'@types/tinycolor2':
specifier: ^1.4.3
version: 1.4.3
autoprefixer:
specifier: ^10.4.14
version: 10.4.14(postcss@8.4.25)
@ -29,6 +38,9 @@ devDependencies:
github-markdown-css:
specifier: ^5.2.0
version: 5.2.0
meshline:
specifier: ^3.1.6
version: 3.1.6(three@0.154.0)
nuxt:
specifier: ^3.6.2
version: 3.6.2(@types/node@18.0.0)(typescript@5.1.6)
@ -44,6 +56,12 @@ devDependencies:
tailwindcss:
specifier: ^3.3.2
version: 3.3.2
three:
specifier: ^0.154.0
version: 0.154.0
tinycolor2:
specifier: ^1.6.0
version: 1.6.0
vite-svg-loader:
specifier: ^4.0.0
version: 4.0.0
@ -1424,6 +1442,17 @@ packages:
- vue-tsc
dev: true
/@nuxtjs/color-mode@3.3.0:
resolution: {integrity: sha512-YVFNmTISke1eL7uk5p9I1suOsM222FxrqKoF13HS4x94OKCWwPLLeTCEzHZ8orzKnaFUbCXpuL4pRv8gvW+0Kw==}
dependencies:
'@nuxt/kit': 3.6.2
lodash.template: 4.5.0
pathe: 1.1.1
transitivePeerDependencies:
- rollup
- supports-color
dev: true
/@nuxtjs/i18n@8.0.0-beta.13(vue@3.3.4):
resolution: {integrity: sha512-h0OqoSSdD9MGCXYZIDpYgQNN90r7MQ/sKVtyBQnrpGQLh1pqI7uLXoAVz4l4r09JzE2nNXK5U0thcx7Tq7ONUg==}
engines: {node: ^14.16.0 || >=16.11.0}
@ -1677,6 +1706,10 @@ packages:
minimatch: 9.0.3
dev: true
/@tweenjs/tween.js@18.6.4:
resolution: {integrity: sha512-lB9lMjuqjtuJrx7/kOkqQBtllspPIN+96OvTCeJ2j5FEzinoAXTdAMFnDAQT1KVPRlnYfBrqxtqP66vDM40xxQ==}
dev: true
/@types/debug@4.1.8:
resolution: {integrity: sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==}
dependencies:
@ -1731,6 +1764,24 @@ packages:
'@types/node': 18.0.0
dev: true
/@types/stats.js@0.17.0:
resolution: {integrity: sha512-9w+a7bR8PeB0dCT/HBULU2fMqf6BAzvKbxFboYhmDtDkKPiyXYbjoe2auwsXlEFI7CFNMF1dCv3dFH5Poy9R1w==}
dev: true
/@types/three@0.153.0:
resolution: {integrity: sha512-L9quzIP4lsl6asDCw5zqN5opewCVOKRuB09apw5Acf2AIkoj5gLnUOY5AMB/ntFUt/QfFey0uKZaAd5t+HXSUw==}
dependencies:
'@tweenjs/tween.js': 18.6.4
'@types/stats.js': 0.17.0
'@types/webxr': 0.5.2
fflate: 0.6.10
lil-gui: 0.17.0
dev: true
/@types/tinycolor2@1.4.3:
resolution: {integrity: sha512-Kf1w9NE5HEgGxCRyIcRXR/ZYtDv0V8FVPtYHwLxl0O+maGX0erE77pQlD0gpP+/KByMZ87mOA79SjifhSB3PjQ==}
dev: true
/@types/unist@2.0.6:
resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==}
dev: true
@ -1739,6 +1790,10 @@ packages:
resolution: {integrity: sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w==}
dev: true
/@types/webxr@0.5.2:
resolution: {integrity: sha512-szL74BnIcok9m7QwYtVmQ+EdIKwbjPANudfuvDrAF8Cljg9MKUlIoc1w5tjj9PMpeSH3U1Xnx//czQybJ0EfSw==}
dev: true
/@unhead/dom@1.1.30:
resolution: {integrity: sha512-EvASOkk36lW5sRfIe+StCojpkPEExsQNt+cqcpdVr9iiRH54jziCDFxcLfjawc+jp4NO86KvmfHo86GIly3/SQ==}
dependencies:
@ -3407,6 +3462,10 @@ packages:
web-streams-polyfill: 3.2.1
dev: true
/fflate@0.6.10:
resolution: {integrity: sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==}
dev: true
/file-type@3.9.0:
resolution: {integrity: sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==}
engines: {node: '>=0.10.0'}
@ -4412,6 +4471,10 @@ packages:
readable-stream: 2.3.8
dev: true
/lil-gui@0.17.0:
resolution: {integrity: sha512-MVBHmgY+uEbmJNApAaPbtvNh1RCAeMnKym82SBjtp5rODTYKWtM+MXHCifLe2H2Ti1HuBGBtK/5SyG4ShQ3pUQ==}
dev: true
/lilconfig@2.1.0:
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
engines: {node: '>=10'}
@ -4439,6 +4502,10 @@ packages:
engines: {node: '>=14'}
dev: true
/lodash._reinterpolate@3.0.0:
resolution: {integrity: sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==}
dev: true
/lodash.debounce@4.0.8:
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
dev: true
@ -4471,6 +4538,19 @@ packages:
resolution: {integrity: sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==}
dev: true
/lodash.template@4.5.0:
resolution: {integrity: sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==}
dependencies:
lodash._reinterpolate: 3.0.0
lodash.templatesettings: 4.2.0
dev: true
/lodash.templatesettings@4.2.0:
resolution: {integrity: sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==}
dependencies:
lodash._reinterpolate: 3.0.0
dev: true
/lodash.union@4.6.0:
resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==}
dev: true
@ -4747,6 +4827,14 @@ packages:
engines: {node: '>= 8'}
dev: true
/meshline@3.1.6(three@0.154.0):
resolution: {integrity: sha512-8JZJOdaL5oz3PI/upG8JvP/5FfzYUOhrkJ8np/WKvXzl0/PZ2V9pqTvCIjSKv+w9ccg2xb+yyBhXAwt6ier3ug==}
peerDependencies:
three: '>=0.137'
dependencies:
three: 0.154.0
dev: true
/micromark-core-commonmark@1.1.0:
resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==}
dependencies:
@ -7141,6 +7229,10 @@ packages:
any-promise: 1.3.0
dev: true
/three@0.154.0:
resolution: {integrity: sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug==}
dev: true
/through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
dev: true
@ -7149,6 +7241,10 @@ packages:
resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
dev: true
/tinycolor2@1.6.0:
resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
dev: true
/titleize@3.0.0:
resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==}
engines: {node: '>=12'}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
public/img/hero/ai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -2,6 +2,7 @@ import type { Config } from "tailwindcss"
import defaultTheme from "tailwindcss/defaultTheme";
export default <Config> {
darkMode: 'class',
content: [
'./components/**/*.{js,vue,ts}',
"./layouts/**/*.vue",
@ -30,6 +31,7 @@ export default <Config> {
},
},
fontFamily: {
'title': ['Capriola', 'GenJyuuGothicX', ...defaultTheme.fontFamily.sans],
'sans': ['Nunito', 'GenJyuuGothicX', ...defaultTheme.fontFamily.sans],
'content-sans': ['Nunito', 'GenJyuuGothicXP', ...defaultTheme.fontFamily.sans],
}