old
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
# EditorConfig is awesome: https://EditorConfig.org
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
# top-most EditorConfig file
|
# top-most EditorConfig file
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
# Unix-style newlines with a newline ending every file
|
# Unix-style newlines with a newline ending every file
|
||||||
[*]
|
[*]
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
46
.gitignore
vendored
46
.gitignore
vendored
@@ -1,23 +1,23 @@
|
|||||||
# build output
|
# build output
|
||||||
dist/
|
dist/
|
||||||
# generated types
|
# generated types
|
||||||
.astro/
|
.astro/
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
# logs
|
# logs
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
pnpm-debug.log*
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
|
||||||
# environment variables
|
# environment variables
|
||||||
.env
|
.env
|
||||||
.env.production
|
.env.production
|
||||||
|
|
||||||
# macOS-specific files
|
# macOS-specific files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
.vscode
|
.vscode
|
||||||
|
|||||||
42
LICENSE
42
LICENSE
@@ -1,21 +1,21 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2024
|
Copyright (c) 2024
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all
|
||||||
copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|||||||
78
README.md
78
README.md
@@ -1,39 +1,39 @@
|
|||||||
# Gblog is an open-source, simple, and beautiful blog built with Astro.
|
# Gblog is an open-source, simple, and beautiful blog built with Astro.
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- 🐈 Simple And Beautiful
|
- 🐈 Simple And Beautiful
|
||||||
- 🖥️️ Responsive And Light/Dark mode
|
- 🖥️️ Responsive And Light/Dark mode
|
||||||
- 🐛 SiteMap & RSS Feed
|
- 🐛 SiteMap & RSS Feed
|
||||||
- 🐝 Category and Timeline Support
|
- 🐝 Category and Timeline Support
|
||||||
- 🍋 Google Analytics & Google Structured Data
|
- 🍋 Google Analytics & Google Structured Data
|
||||||
- 🐜 SEO and Responsiveness
|
- 🐜 SEO and Responsiveness
|
||||||
- 🪲 Markdown And MDX
|
- 🪲 Markdown And MDX
|
||||||
- 🏂🏾 Page Compression & Image Optimization
|
- 🏂🏾 Page Compression & Image Optimization
|
||||||
|
|
||||||
## Make Your Own
|
## Make Your Own
|
||||||
|
|
||||||
### Build from Source
|
### Build from Source
|
||||||
|
|
||||||
1. Clone the `gblog-template` branch of this repository `git clone -b gblog-template git@github.com:godruoyi/gblog.git`
|
1. Clone the `gblog-template` branch of this repository `git clone -b gblog-template git@github.com:godruoyi/gblog.git`
|
||||||
2. Execute `pnpm install` to install dependencies.
|
2. Execute `pnpm install` to install dependencies.
|
||||||
3. Modify the `src/config.ts` file to what you want.
|
3. Modify the `src/config.ts` file to what you want.
|
||||||
4. Execute `pnpm run dev`: Starts a local development server with hot reloading enabled.
|
4. Execute `pnpm run dev`: Starts a local development server with hot reloading enabled.
|
||||||
|
|
||||||
### Build from Astro Template(coming soon)
|
### Build from Astro Template(coming soon)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Development Commands
|
### Development Commands
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
||||||
Thanks https://github.com/mearashadowfax/ScrewFast, The majority of the code for this project comes from ScrewFast.
|
Thanks https://github.com/mearashadowfax/ScrewFast, The majority of the code for this project comes from ScrewFast.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is released under the MIT License.
|
This project is released under the MIT License.
|
||||||
|
|||||||
@@ -1,41 +1,41 @@
|
|||||||
import { defineConfig } from 'astro/config'
|
import { defineConfig } from 'astro/config'
|
||||||
import mdx from '@astrojs/mdx'
|
import mdx from '@astrojs/mdx'
|
||||||
import tailwind from '@astrojs/tailwind'
|
import tailwind from '@astrojs/tailwind'
|
||||||
import react from '@astrojs/react'
|
import react from '@astrojs/react'
|
||||||
import sitemap from '@astrojs/sitemap'
|
import sitemap from '@astrojs/sitemap'
|
||||||
import { SITE } from './src/config.ts'
|
import { SITE } from './src/config.ts'
|
||||||
import { remarkReadingTime } from './src/support/time.ts'
|
import { remarkReadingTime } from './src/support/time.ts'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: SITE.url,
|
site: SITE.url,
|
||||||
image: {},
|
image: {},
|
||||||
integrations: [
|
integrations: [
|
||||||
mdx(),
|
mdx(),
|
||||||
sitemap(),
|
sitemap(),
|
||||||
tailwind(),
|
tailwind(),
|
||||||
react(),
|
react(),
|
||||||
(await import('@playform/compress')).default({
|
(await import('@playform/compress')).default({
|
||||||
CSS: true,
|
CSS: true,
|
||||||
HTML: true,
|
HTML: true,
|
||||||
Image: false,
|
Image: false,
|
||||||
JavaScript: true,
|
JavaScript: true,
|
||||||
SVG: true,
|
SVG: true,
|
||||||
Logger: 2,
|
Logger: 2,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
markdown: {
|
markdown: {
|
||||||
remarkPlugins: [remarkReadingTime],
|
remarkPlugins: [remarkReadingTime],
|
||||||
shikiConfig: {
|
shikiConfig: {
|
||||||
themes: {
|
themes: {
|
||||||
light: 'material-theme-lighter',
|
light: 'material-theme-lighter',
|
||||||
dark: 'one-dark-pro',
|
dark: 'one-dark-pro',
|
||||||
},
|
},
|
||||||
wrap: false,
|
wrap: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output: 'static',
|
output: 'static',
|
||||||
// experimental: {
|
// experimental: {
|
||||||
// clientPrerender: true,
|
// clientPrerender: true,
|
||||||
// directRenderScript: true,
|
// directRenderScript: true,
|
||||||
// },
|
// },
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import antfu from '@antfu/eslint-config'
|
import antfu from '@antfu/eslint-config'
|
||||||
|
|
||||||
export default antfu({
|
export default antfu({
|
||||||
stylistic: {
|
stylistic: {
|
||||||
indent: 4, // 4, or 'tab'
|
indent: 4, // 4, or 'tab'
|
||||||
quotes: 'single', // or 'double'
|
quotes: 'single', // or 'double'
|
||||||
},
|
},
|
||||||
|
|
||||||
react: true,
|
react: true,
|
||||||
typescript: true,
|
typescript: true,
|
||||||
vue: false,
|
vue: false,
|
||||||
astro: true,
|
astro: true,
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
'no-console': 'off',
|
'no-console': 'off',
|
||||||
'curly': ['error', 'all'],
|
'curly': ['error', 'all'],
|
||||||
'node/prefer-global/process': 'off',
|
'node/prefer-global/process': 'off',
|
||||||
},
|
},
|
||||||
|
|
||||||
jsonc: false,
|
jsonc: false,
|
||||||
yaml: false,
|
yaml: false,
|
||||||
})
|
})
|
||||||
|
|||||||
29938
package-lock.json
generated
29938
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
2
public/vendor/lenis/lenis1.0.42.min.js
vendored
2
public/vendor/lenis/lenis1.0.42.min.js
vendored
File diff suppressed because one or more lines are too long
46
public/vendor/preline/collapse2.1.0.min.js
vendored
46
public/vendor/preline/collapse2.1.0.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,173 +1,173 @@
|
|||||||
/**
|
/**
|
||||||
* Load all posts from my blog database and write them to Markdown file
|
* Load all posts from my blog database and write them to Markdown file
|
||||||
*
|
*
|
||||||
* usage:
|
* usage:
|
||||||
*
|
*
|
||||||
* node db_to_md.js db_username db_password
|
* node db_to_md.js db_username db_password
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { dirname, join } from 'node:path'
|
import { dirname, join } from 'node:path'
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
import * as fs from 'node:fs'
|
import * as fs from 'node:fs'
|
||||||
import mysql from 'mysql2/promise'
|
import mysql from 'mysql2/promise'
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url)
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
const __dirname = dirname(__filename)
|
const __dirname = dirname(__filename)
|
||||||
|
|
||||||
class BlogToMarkdown {
|
class BlogToMarkdown {
|
||||||
databaseName = 'godruoyi'
|
databaseName = 'godruoyi'
|
||||||
|
|
||||||
host = '127.0.0.1'
|
host = '127.0.0.1'
|
||||||
|
|
||||||
port = 13306
|
port = 13306
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
const { username, password } = this.parseProcessArgs()
|
const { username, password } = this.parseProcessArgs()
|
||||||
|
|
||||||
this.username = username
|
this.username = username
|
||||||
this.password = password
|
this.password = password
|
||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
this.connection = await this.connectMySQL(this.username, this.password)
|
this.connection = await this.connectMySQL(this.username, this.password)
|
||||||
|
|
||||||
const categoryMap = await this.fetchCategories()
|
const categoryMap = await this.fetchCategories()
|
||||||
const posts = await this.fetchPosts()
|
const posts = await this.fetchPosts()
|
||||||
|
|
||||||
const formatedPosts = this.formatPosts(categoryMap, posts)
|
const formatedPosts = this.formatPosts(categoryMap, posts)
|
||||||
|
|
||||||
for (const p of formatedPosts) {
|
for (const p of formatedPosts) {
|
||||||
// this.writePostToMarkdown(p)
|
// this.writePostToMarkdown(p)
|
||||||
await this.savePostBanner(p)
|
await this.savePostBanner(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('All done')
|
console.log('All done')
|
||||||
}
|
}
|
||||||
|
|
||||||
async savePostBanner(post) {
|
async savePostBanner(post) {
|
||||||
const filePath = this.postBannerFilePath(post)
|
const filePath = this.postBannerFilePath(post)
|
||||||
console.log('savePostBanner', filePath)
|
console.log('savePostBanner', filePath)
|
||||||
|
|
||||||
const response = await fetch(post.banner)
|
const response = await fetch(post.banner)
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('download image failed')
|
throw new Error('download image failed')
|
||||||
}
|
}
|
||||||
|
|
||||||
const arrayBuffer = await response.arrayBuffer()
|
const arrayBuffer = await response.arrayBuffer()
|
||||||
fs.writeFileSync(filePath, Buffer.from(arrayBuffer))
|
fs.writeFileSync(filePath, Buffer.from(arrayBuffer))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchPosts() {
|
async fetchPosts() {
|
||||||
const query = 'SELECT * FROM posts ORDER BY id ASC'
|
const query = 'SELECT * FROM posts ORDER BY id ASC'
|
||||||
|
|
||||||
const [rows] = await this.connection.execute(query)
|
const [rows] = await this.connection.execute(query)
|
||||||
|
|
||||||
return rows
|
return rows
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchCategories() {
|
async fetchCategories() {
|
||||||
const query = 'SELECT * FROM categories ORDER BY id ASC'
|
const query = 'SELECT * FROM categories ORDER BY id ASC'
|
||||||
|
|
||||||
const [rows] = await this.connection.execute(query)
|
const [rows] = await this.connection.execute(query)
|
||||||
|
|
||||||
return rows.reduce((map, obj) => map.set(obj.id, obj), new Map())
|
return rows.reduce((map, obj) => map.set(obj.id, obj), new Map())
|
||||||
}
|
}
|
||||||
|
|
||||||
writePostToMarkdown(post) {
|
writePostToMarkdown(post) {
|
||||||
console.log(`saving post to markdown: ${post.title}`)
|
console.log(`saving post to markdown: ${post.title}`)
|
||||||
|
|
||||||
const file = this.postToMarkdownFilePath(post)
|
const file = this.postToMarkdownFilePath(post)
|
||||||
const content = this.convertPostToMarkdown(post)
|
const content = this.convertPostToMarkdown(post)
|
||||||
|
|
||||||
fs.writeFileSync(file, content, 'utf-8')
|
fs.writeFileSync(file, content, 'utf-8')
|
||||||
}
|
}
|
||||||
|
|
||||||
postToMarkdownFilePath(p) {
|
postToMarkdownFilePath(p) {
|
||||||
return join(__dirname, `./../src/content/posts/${p.slug}.md`)
|
return join(__dirname, `./../src/content/posts/${p.slug}.md`)
|
||||||
}
|
}
|
||||||
|
|
||||||
postBannerFilePath(p) {
|
postBannerFilePath(p) {
|
||||||
const name = new URL(p.banner).pathname.split('/').pop()
|
const name = new URL(p.banner).pathname.split('/').pop()
|
||||||
return join(__dirname, `./../src/images/posts/${name}`)
|
return join(__dirname, `./../src/images/posts/${name}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
convertPostToMarkdown(post) {
|
convertPostToMarkdown(post) {
|
||||||
return `---
|
return `---
|
||||||
title: "${post.title}"
|
title: "${post.title}"
|
||||||
description: "${post.description}"
|
description: "${post.description}"
|
||||||
pubDate: "${this.formatDate(post.pubDate)}"
|
pubDate: "${this.formatDate(post.pubDate)}"
|
||||||
category: "${post.category.slug}"
|
category: "${post.category.slug}"
|
||||||
cardImage: "${post.banner}"
|
cardImage: "${post.banner}"
|
||||||
tags: ["${post.category.slug}"]
|
tags: ["${post.category.slug}"]
|
||||||
oldViewCount: ${post.viewCount}
|
oldViewCount: ${post.viewCount}
|
||||||
oldKeywords: ["${post.keyword}"]
|
oldKeywords: ["${post.keyword}"]
|
||||||
---
|
---
|
||||||
|
|
||||||
${post.content}
|
${post.content}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
formatPosts(categoryMap, posts) {
|
formatPosts(categoryMap, posts) {
|
||||||
return posts.map((p) => {
|
return posts.map((p) => {
|
||||||
return {
|
return {
|
||||||
title: p.title,
|
title: p.title,
|
||||||
description: p.excerpt,
|
description: p.excerpt,
|
||||||
pubDate: p.created_at,
|
pubDate: p.created_at,
|
||||||
banner: p.banner,
|
banner: p.banner,
|
||||||
category_id: p.category_id,
|
category_id: p.category_id,
|
||||||
category: categoryMap.get(p.category_id),
|
category: categoryMap.get(p.category_id),
|
||||||
slug: p.slug,
|
slug: p.slug,
|
||||||
viewCount: p.view_count,
|
viewCount: p.view_count,
|
||||||
content: p.content,
|
content: p.content,
|
||||||
id: p.id,
|
id: p.id,
|
||||||
keyword: p.keyword,
|
keyword: p.keyword,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async connectMySQL(username, password) {
|
async connectMySQL(username, password) {
|
||||||
return mysql.createConnection({
|
return mysql.createConnection({
|
||||||
host: this.host,
|
host: this.host,
|
||||||
port: this.port,
|
port: this.port,
|
||||||
user: username,
|
user: username,
|
||||||
password,
|
password,
|
||||||
database: this.databaseName,
|
database: this.databaseName,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
closeConnection() {
|
closeConnection() {
|
||||||
if (this.connection) {
|
if (this.connection) {
|
||||||
this.connection.close()
|
this.connection.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseProcessArgs() {
|
parseProcessArgs() {
|
||||||
const username = process.argv[2]
|
const username = process.argv[2]
|
||||||
const password = process.argv[3]
|
const password = process.argv[3]
|
||||||
|
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
throw new Error('Missing username or password')
|
throw new Error('Missing username or password')
|
||||||
}
|
}
|
||||||
|
|
||||||
return { username, password }
|
return { username, password }
|
||||||
}
|
}
|
||||||
|
|
||||||
formatDate(date) {
|
formatDate(date) {
|
||||||
const newDate = new Date(date.getTime() + 8 * 60 * 60 * 1000) // Date object uses milliseconds
|
const newDate = new Date(date.getTime() + 8 * 60 * 60 * 1000) // Date object uses milliseconds
|
||||||
|
|
||||||
const yyyy = newDate.getUTCFullYear()
|
const yyyy = newDate.getUTCFullYear()
|
||||||
const mm = String(newDate.getUTCMonth() + 1).padStart(2, '0') // Months are zero based
|
const mm = String(newDate.getUTCMonth() + 1).padStart(2, '0') // Months are zero based
|
||||||
const dd = String(newDate.getUTCDate()).padStart(2, '0')
|
const dd = String(newDate.getUTCDate()).padStart(2, '0')
|
||||||
|
|
||||||
const hh = String(newDate.getUTCHours()).padStart(2, '0')
|
const hh = String(newDate.getUTCHours()).padStart(2, '0')
|
||||||
const mi = String(newDate.getUTCMinutes()).padStart(2, '0')
|
const mi = String(newDate.getUTCMinutes()).padStart(2, '0')
|
||||||
const ss = String(newDate.getUTCSeconds()).padStart(2, '0')
|
const ss = String(newDate.getUTCSeconds()).padStart(2, '0')
|
||||||
|
|
||||||
return `${yyyy}-${mm}-${dd} ${hh}:${mi}:${ss}`
|
return `${yyyy}-${mm}-${dd} ${hh}:${mi}:${ss}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const x = new BlogToMarkdown()
|
const x = new BlogToMarkdown()
|
||||||
await x.run()
|
await x.run()
|
||||||
await process.exit(0)
|
await process.exit(0)
|
||||||
|
|||||||
@@ -1,88 +1,88 @@
|
|||||||
---
|
---
|
||||||
import faviconSvgSrc from '@images/icon.svg'
|
import faviconSvgSrc from '@images/icon.svg'
|
||||||
import { getImage } from 'astro:assets'
|
import { getImage } from 'astro:assets'
|
||||||
import faviconSrc from '@images/icon.png'
|
import faviconSrc from '@images/icon.png'
|
||||||
import brandSrc from '@images/brand-logo.jpeg'
|
import brandSrc from '@images/brand-logo.jpeg'
|
||||||
import { SEO, SITE } from '../config'
|
import { SEO, SITE } from '../config'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
ogImage?: any
|
ogImage?: any
|
||||||
ogTitle?: string
|
ogTitle?: string
|
||||||
ogDescription?: string
|
ogDescription?: string
|
||||||
structuredData?: object
|
structuredData?: object
|
||||||
}
|
}
|
||||||
|
|
||||||
const canonicalURL = Astro.url.href
|
const canonicalURL = Astro.url.href
|
||||||
let {
|
let {
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
ogImage,
|
ogImage,
|
||||||
ogTitle = title,
|
ogTitle = title,
|
||||||
ogDescription = description,
|
ogDescription = description,
|
||||||
structuredData = SEO.structuredData,
|
structuredData = SEO.structuredData,
|
||||||
} = Astro.props
|
} = Astro.props
|
||||||
|
|
||||||
let card = 'summary_large_image'
|
let card = 'summary_large_image'
|
||||||
if (!ogImage) {
|
if (!ogImage) {
|
||||||
ogImage = brandSrc
|
ogImage = brandSrc
|
||||||
card = 'summary'
|
card = 'summary'
|
||||||
}
|
}
|
||||||
|
|
||||||
const faviconSvg = await getImage({ src: faviconSvgSrc, format: 'svg' })
|
const faviconSvg = await getImage({ src: faviconSvgSrc, format: 'svg' })
|
||||||
const appleTouchIcon = await getImage({ src: faviconSrc, width: 180, height: 180, format: 'png' })
|
const appleTouchIcon = await getImage({ src: faviconSrc, width: 180, height: 180, format: 'png' })
|
||||||
const socialImageRes = await getImage({ src: ogImage, width: 1200, height: 600 })
|
const socialImageRes = await getImage({ src: ogImage, width: 1200, height: 600 })
|
||||||
const socialImage = Astro.url.origin + socialImageRes.src
|
const socialImage = Astro.url.origin + socialImageRes.src
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- Inject structured data https://developers.google.com/search/docs/advanced/structured-data/intro-structured-data -->
|
<!-- Inject structured data https://developers.google.com/search/docs/advanced/structured-data/intro-structured-data -->
|
||||||
{
|
{
|
||||||
structuredData && (
|
structuredData && (
|
||||||
<script
|
<script
|
||||||
type="application/ld+json"
|
type="application/ld+json"
|
||||||
set:html={JSON.stringify(structuredData)}
|
set:html={JSON.stringify(structuredData)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- Global Metadata -->
|
<!-- Global Metadata -->
|
||||||
<meta name="title" content={title} />
|
<meta name="title" content={title} />
|
||||||
<meta name="description" content={description} />
|
<meta name="description" content={description} />
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="web_author" content={SITE.author} />
|
<meta name="web_author" content={SITE.author} />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, minimum-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, minimum-scale=1.0" />
|
||||||
<meta name="generator" content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||||
<meta name="mobile-web-app-capable" content="yes" />
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
<meta name="theme-color" content="#facc15" />
|
<meta name="theme-color" content="#facc15" />
|
||||||
|
|
||||||
<!-- Open Graph -->
|
<!-- Open Graph -->
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta property="og:url" content={Astro.url} />
|
<meta property="og:url" content={Astro.url} />
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:title" content={ogTitle} />
|
<meta property="og:title" content={ogTitle} />
|
||||||
<meta property="og:site_name" content={SITE.title} />
|
<meta property="og:site_name" content={SITE.title} />
|
||||||
<meta property="og:description" content={ogDescription} />
|
<meta property="og:description" content={ogDescription} />
|
||||||
<meta property="og:image" content={socialImage} />
|
<meta property="og:image" content={socialImage} />
|
||||||
<meta content="1200" property="og:image:width" />
|
<meta content="1200" property="og:image:width" />
|
||||||
<meta content="600" property="og:image:height" />
|
<meta content="600" property="og:image:height" />
|
||||||
<meta content="image/png" property="og:image:type" />
|
<meta content="image/png" property="og:image:type" />
|
||||||
|
|
||||||
<!-- Twitter -->
|
<!-- Twitter -->
|
||||||
<meta property="twitter:card" content={card} />
|
<meta property="twitter:card" content={card} />
|
||||||
<meta property="twitter:url" content={Astro.url} />
|
<meta property="twitter:url" content={Astro.url} />
|
||||||
<meta property="twitter:domain" content={Astro.url} />
|
<meta property="twitter:domain" content={Astro.url} />
|
||||||
<meta property="twitter:title" content={ogTitle} />
|
<meta property="twitter:title" content={ogTitle} />
|
||||||
<meta property="twitter:description" content={ogDescription} />
|
<meta property="twitter:description" content={ogDescription} />
|
||||||
<meta property="twitter:image" content={socialImage} />
|
<meta property="twitter:image" content={socialImage} />
|
||||||
|
|
||||||
<!-- Links -->
|
<!-- Links -->
|
||||||
<link href={canonicalURL} rel="canonical" />
|
<link href={canonicalURL} rel="canonical" />
|
||||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||||
<!--<link href="/manifest.json" rel="manifest" />-->
|
<!--<link href="/manifest.json" rel="manifest" />-->
|
||||||
<link href="/favicon.ico" rel="icon" sizes="any" type="image/x-icon" />
|
<link href="/favicon.ico" rel="icon" sizes="any" type="image/x-icon" />
|
||||||
<link href={faviconSvg.src} rel="icon" type="image/svg+xml" sizes="any" />
|
<link href={faviconSvg.src} rel="icon" type="image/svg+xml" sizes="any" />
|
||||||
<link href={appleTouchIcon.src} rel="apple-touch-icon" />
|
<link href={appleTouchIcon.src} rel="apple-touch-icon" />
|
||||||
<link href={appleTouchIcon.src} rel="shortcut icon" />
|
<link href={appleTouchIcon.src} rel="shortcut icon" />
|
||||||
|
|||||||
@@ -1,54 +1,54 @@
|
|||||||
---
|
---
|
||||||
import BrandLogo from '@components/logos/BrandLogo.astro'
|
import BrandLogo from '@components/logos/BrandLogo.astro'
|
||||||
import { FooterLinks, SITE } from '../config'
|
import { FooterLinks, SITE } from '../config'
|
||||||
---
|
---
|
||||||
|
|
||||||
<footer class="w-full bg-neutral-300 dark:bg-neutral-900">
|
<footer class="w-full bg-neutral-300 dark:bg-neutral-900">
|
||||||
<div class="mx-auto w-full max-w-[85rem] px-4 py-10 sm:px-6 lg:px-16 lg:pt-20 2xl:max-w-screen-2xl">
|
<div class="mx-auto w-full max-w-[85rem] px-4 py-10 sm:px-6 lg:px-16 lg:pt-20 2xl:max-w-screen-2xl">
|
||||||
<div class="grid grid-cols-2 sm:grid-cols-3 gap-6 md:grid-cols-5">
|
<div class="grid grid-cols-2 sm:grid-cols-3 gap-6 md:grid-cols-5">
|
||||||
<div class="col-span-full flex justify-center sm:grid sm:col-span-1">
|
<div class="col-span-full flex justify-center sm:grid sm:col-span-1">
|
||||||
<BrandLogo class="h-auto w-28 rounded-full" />
|
<BrandLogo class="h-auto w-28 rounded-full" />
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
FooterLinks.map(section => (
|
FooterLinks.map(section => (
|
||||||
<div class="col-span-1 text-center">
|
<div class="col-span-1 text-center">
|
||||||
<h3 class="font-bold text-neutral-800 dark:text-neutral-200">
|
<h3 class="font-bold text-neutral-800 dark:text-neutral-200">
|
||||||
{section.section}
|
{section.section}
|
||||||
</h3>
|
</h3>
|
||||||
<ul class="mt-3 grid space-y-3">
|
<ul class="mt-3 grid space-y-3">
|
||||||
{section.links.map((link, _index) => (
|
{section.links.map((link, _index) => (
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href={link.url}
|
href={link.url}
|
||||||
class="inline-flex gap-x-2 rounded-lg text-neutral-600 outline-none ring-zinc-500 transition duration-300 hover:text-neutral-500 focus-visible:ring dark:text-neutral-400 dark:ring-zinc-200 dark:hover:text-neutral-300 dark:focus:outline-none"
|
class="inline-flex gap-x-2 rounded-lg text-neutral-600 outline-none ring-zinc-500 transition duration-300 hover:text-neutral-500 focus-visible:ring dark:text-neutral-400 dark:ring-zinc-200 dark:hover:text-neutral-300 dark:focus:outline-none"
|
||||||
>
|
>
|
||||||
{link.name}
|
{link.name}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="col-span-2 flex justify-center mt-10 md:mt-0">
|
<div class="col-span-2 flex justify-center mt-10 md:mt-0">
|
||||||
<div class="max-w-[320px] max-h-[320px] -mt-16 scale-75 hidden md:block">
|
<div class="max-w-[320px] max-h-[320px] -mt-16 scale-75 hidden md:block">
|
||||||
<!--Image
|
<!--Image
|
||||||
src={ponyo}
|
src={ponyo}
|
||||||
alt={SITE.title}
|
alt={SITE.title}
|
||||||
class="h-full w-full object-cover object-center"
|
class="h-full w-full object-cover object-center"
|
||||||
draggable="false"
|
draggable="false"
|
||||||
loading="eager"
|
loading="eager"
|
||||||
/-->
|
/-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="flex items-center text-center justify-center">
|
<div class="flex items-center text-center justify-center">
|
||||||
<p class="text-sm text-neutral-600 dark:text-neutral-400">
|
<p class="text-sm text-neutral-600 dark:text-neutral-400">
|
||||||
© <span id="current-year">2024</span> {SITE.title}
|
© <span id="current-year">2024</span> {SITE.title}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
---
|
---
|
||||||
interface Props {
|
interface Props {
|
||||||
date: Date
|
date: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
const { date } = Astro.props;
|
const { date } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<time datetime={date.toISOString()}>
|
<time datetime={date.toISOString()}>
|
||||||
{
|
{
|
||||||
date.toLocaleDateString('ru-ru', {
|
date.toLocaleDateString('ru-ru', {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</time>
|
</time>
|
||||||
|
|||||||
@@ -1,184 +1,184 @@
|
|||||||
---
|
---
|
||||||
import { NavigationLinks } from '../config'
|
import { NavigationLinks } from '../config'
|
||||||
import NavLink from './ui/NavLink.astro'
|
import NavLink from './ui/NavLink.astro'
|
||||||
import BrandLogo from './logos/BrandLogo.astro'
|
import BrandLogo from './logos/BrandLogo.astro'
|
||||||
import Theme from './logos/Theme.astro';
|
import Theme from './logos/Theme.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
<header class="sticky inset-x-0 top-4 z-50 flex w-full flex-wrap text-sm md:flex-nowrap md:justify-start">
|
<header class="sticky inset-x-0 top-4 z-50 flex w-full flex-wrap text-sm md:flex-nowrap md:justify-start">
|
||||||
<nav
|
<nav
|
||||||
class="relative mx-2 w-full rounded-[36px] border border-neutral-100 bg-neutral-100 px-4 py-3 backdrop-blur-md dark:border-neutral-700/40 dark:bg-neutral-800/80 dark:backdrop-blur-md md:flex md:items-center md:justify-between md:px-6 lg:px-8 xl:mx-auto"
|
class="relative mx-2 w-full rounded-[36px] border border-neutral-100 bg-neutral-100 px-4 py-3 backdrop-blur-md dark:border-neutral-700/40 dark:bg-neutral-800/80 dark:backdrop-blur-md md:flex md:items-center md:justify-between md:px-6 lg:px-8 xl:mx-auto"
|
||||||
aria-label="Global"
|
aria-label="Global"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between ">
|
<div class="flex items-center justify-between ">
|
||||||
<a
|
<a
|
||||||
class="h-[42px] flex-none rounded-lg text-xl font-bold outline-none ring-zinc-500 focus-visible:ring dark:ring-zinc-200 dark:focus:outline-none"
|
class="h-[42px] flex-none rounded-lg text-xl font-bold outline-none ring-zinc-500 focus-visible:ring dark:ring-zinc-200 dark:focus:outline-none"
|
||||||
href="/"
|
href="/"
|
||||||
aria-label="Brand"
|
aria-label="Brand"
|
||||||
>
|
>
|
||||||
<BrandLogo class="h-full w-auto object-cover rounded-full" />
|
<BrandLogo class="h-full w-auto object-cover rounded-full" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="ml-auto md:hidden">
|
<div class="ml-auto md:hidden">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="hs-collapse-toggle flex h-8 w-8 items-center justify-center rounded-full text-sm font-bold text-neutral-600 transition duration-300 hover:bg-neutral-200 disabled:pointer-events-none disabled:opacity-50 dark:text-neutral-400 dark:hover:bg-neutral-700 dark:focus:outline-none"
|
class="hs-collapse-toggle flex h-8 w-8 items-center justify-center rounded-full text-sm font-bold text-neutral-600 transition duration-300 hover:bg-neutral-200 disabled:pointer-events-none disabled:opacity-50 dark:text-neutral-400 dark:hover:bg-neutral-700 dark:focus:outline-none"
|
||||||
data-hs-collapse="#navbar-collapse-with-animation"
|
data-hs-collapse="#navbar-collapse-with-animation"
|
||||||
aria-controls="navbar-collapse-with-animation"
|
aria-controls="navbar-collapse-with-animation"
|
||||||
aria-label="Toggle navigation"
|
aria-label="Toggle navigation"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="h-[1.25rem] w-[1.25rem] flex-shrink-0 hs-collapse-open:hidden"
|
class="h-[1.25rem] w-[1.25rem] flex-shrink-0 hs-collapse-open:hidden"
|
||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<line x1="3" x2="21" y1="6" y2="6"></line>
|
<line x1="3" x2="21" y1="6" y2="6"></line>
|
||||||
<line x1="3" x2="21" y1="12" y2="12"></line>
|
<line x1="3" x2="21" y1="12" y2="12"></line>
|
||||||
<line x1="3" x2="21" y1="18" y2="18"></line>
|
<line x1="3" x2="21" y1="18" y2="18"></line>
|
||||||
</svg>
|
</svg>
|
||||||
<svg
|
<svg
|
||||||
class="hidden h-[1.25rem] w-[1.25rem] flex-shrink-0 hs-collapse-open:block"
|
class="hidden h-[1.25rem] w-[1.25rem] flex-shrink-0 hs-collapse-open:block"
|
||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<path d="M18 6 6 18"></path>
|
<path d="M18 6 6 18"></path>
|
||||||
<path d="m6 6 12 12"></path>
|
<path d="m6 6 12 12"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<span class="inline-block md:hidden">
|
<span class="inline-block md:hidden">
|
||||||
<Theme />
|
<Theme />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="navbar-collapse-with-animation"
|
id="navbar-collapse-with-animation"
|
||||||
class="hs-collapse hidden grow basis-full overflow-hidden transition-all duration-300 md:block"
|
class="hs-collapse hidden grow basis-full overflow-hidden transition-all duration-300 md:block"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mt-5 flex flex-col gap-x-0 gap-y-4 md:mt-0 md:flex-row md:items-center md:justify-end md:gap-x-4 lg:gap-x-7 md:gap-y-0 md:ps-7"
|
class="mt-5 flex flex-col gap-x-0 gap-y-4 md:mt-0 md:flex-row md:items-center md:justify-end md:gap-x-4 lg:gap-x-7 md:gap-y-0 md:ps-7"
|
||||||
>
|
>
|
||||||
{NavigationLinks.map(link => (
|
{NavigationLinks.map(link => (
|
||||||
<NavLink url={link.url} name={link.name} />
|
<NavLink url={link.url} name={link.name} />
|
||||||
))}
|
))}
|
||||||
<span class="hidden md:inline-block">
|
<span class="hidden md:inline-block">
|
||||||
<Theme />
|
<Theme />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
const HSThemeAppearance = {
|
const HSThemeAppearance = {
|
||||||
init() {
|
init() {
|
||||||
const defaultTheme = "default";
|
const defaultTheme = "default";
|
||||||
let theme = localStorage.getItem("hs_theme") || defaultTheme;
|
let theme = localStorage.getItem("hs_theme") || defaultTheme;
|
||||||
|
|
||||||
if (document.querySelector("html").classList.contains("dark")) {
|
if (document.querySelector("html").classList.contains("dark")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setAppearance(theme);
|
this.setAppearance(theme);
|
||||||
},
|
},
|
||||||
|
|
||||||
_resetStylesOnLoad() {
|
_resetStylesOnLoad() {
|
||||||
const $resetStyles = document.createElement("style");
|
const $resetStyles = document.createElement("style");
|
||||||
$resetStyles.innerText = `*{transition: unset !important;}`;
|
$resetStyles.innerText = `*{transition: unset !important;}`;
|
||||||
$resetStyles.setAttribute("data-hs-appearance-onload-styles", "");
|
$resetStyles.setAttribute("data-hs-appearance-onload-styles", "");
|
||||||
document.head.appendChild($resetStyles);
|
document.head.appendChild($resetStyles);
|
||||||
return $resetStyles;
|
return $resetStyles;
|
||||||
},
|
},
|
||||||
|
|
||||||
setAppearance(theme, saveInStore = true, dispatchEvent = true) {
|
setAppearance(theme, saveInStore = true, dispatchEvent = true) {
|
||||||
const $resetStylesEl = this._resetStylesOnLoad();
|
const $resetStylesEl = this._resetStylesOnLoad();
|
||||||
|
|
||||||
if (saveInStore) {
|
if (saveInStore) {
|
||||||
localStorage.setItem("hs_theme", theme);
|
localStorage.setItem("hs_theme", theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theme === "auto") {
|
if (theme === "auto") {
|
||||||
theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "default";
|
theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "default";
|
||||||
}
|
}
|
||||||
|
|
||||||
document.querySelector("html").classList.remove("dark");
|
document.querySelector("html").classList.remove("dark");
|
||||||
document.querySelector("html").classList.remove("default");
|
document.querySelector("html").classList.remove("default");
|
||||||
document.querySelector("html").classList.remove("auto");
|
document.querySelector("html").classList.remove("auto");
|
||||||
|
|
||||||
document.querySelector("html").classList.add(this.getOriginalAppearance());
|
document.querySelector("html").classList.add(this.getOriginalAppearance());
|
||||||
|
|
||||||
setTimeout(() => $resetStylesEl.remove());
|
setTimeout(() => $resetStylesEl.remove());
|
||||||
|
|
||||||
if (dispatchEvent) {
|
if (dispatchEvent) {
|
||||||
window.dispatchEvent(new CustomEvent("on-hs-appearance-change", { detail: theme }));
|
window.dispatchEvent(new CustomEvent("on-hs-appearance-change", { detail: theme }));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getAppearance() {
|
getAppearance() {
|
||||||
let theme = this.getOriginalAppearance();
|
let theme = this.getOriginalAppearance();
|
||||||
if (theme === "auto") {
|
if (theme === "auto") {
|
||||||
theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "default";
|
theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "default";
|
||||||
}
|
}
|
||||||
|
|
||||||
return theme;
|
return theme;
|
||||||
},
|
},
|
||||||
|
|
||||||
getOriginalAppearance() {
|
getOriginalAppearance() {
|
||||||
const defaultTheme = "default";
|
const defaultTheme = "default";
|
||||||
return localStorage.getItem("hs_theme") || defaultTheme;
|
return localStorage.getItem("hs_theme") || defaultTheme;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
HSThemeAppearance.init();
|
HSThemeAppearance.init();
|
||||||
|
|
||||||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
||||||
if (HSThemeAppearance.getOriginalAppearance() === "auto") {
|
if (HSThemeAppearance.getOriginalAppearance() === "auto") {
|
||||||
HSThemeAppearance.setAppearance("auto", false);
|
HSThemeAppearance.setAppearance("auto", false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("load", () => {
|
window.addEventListener("load", () => {
|
||||||
const $clickableThemes = document.querySelectorAll(
|
const $clickableThemes = document.querySelectorAll(
|
||||||
"[data-hs-theme-click-value]",
|
"[data-hs-theme-click-value]",
|
||||||
);
|
);
|
||||||
const $switchableThemes = document.querySelectorAll(
|
const $switchableThemes = document.querySelectorAll(
|
||||||
"[data-hs-theme-switch]",
|
"[data-hs-theme-switch]",
|
||||||
);
|
);
|
||||||
|
|
||||||
$clickableThemes.forEach(($item) => {
|
$clickableThemes.forEach(($item) => {
|
||||||
$item.addEventListener("click", () =>
|
$item.addEventListener("click", () =>
|
||||||
HSThemeAppearance.setAppearance(
|
HSThemeAppearance.setAppearance(
|
||||||
$item.getAttribute("data-hs-theme-click-value"),
|
$item.getAttribute("data-hs-theme-click-value"),
|
||||||
true,
|
true,
|
||||||
$item,
|
$item,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
$switchableThemes.forEach(($item) => {
|
$switchableThemes.forEach(($item) => {
|
||||||
$item.addEventListener("change", (e) => {
|
$item.addEventListener("change", (e) => {
|
||||||
HSThemeAppearance.setAppearance(e.target.checked ? "dark" : "default");
|
HSThemeAppearance.setAppearance(e.target.checked ? "dark" : "default");
|
||||||
});
|
});
|
||||||
|
|
||||||
$item.checked = HSThemeAppearance.getAppearance() === "dark";
|
$item.checked = HSThemeAppearance.getAppearance() === "dark";
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("on-hs-appearance-change", (e) => {
|
window.addEventListener("on-hs-appearance-change", (e) => {
|
||||||
$switchableThemes.forEach(($item) => {
|
$switchableThemes.forEach(($item) => {
|
||||||
$item.checked = e.detail === "dark";
|
$item.checked = e.detail === "dark";
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<script is:inline src="/vendor/preline/collapse2.1.0.min.js"></script>
|
<script is:inline src="/vendor/preline/collapse2.1.0.min.js"></script>
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
---
|
---
|
||||||
import type { HTMLAttributes } from 'astro/types'
|
import type { HTMLAttributes } from 'astro/types'
|
||||||
|
|
||||||
type Props = HTMLAttributes<'a'>
|
type Props = HTMLAttributes<'a'>
|
||||||
|
|
||||||
const { href, class: className, ...props } = Astro.props
|
const { href, class: className, ...props } = Astro.props
|
||||||
|
|
||||||
const { pathname } = Astro.url
|
const { pathname } = Astro.url
|
||||||
const isActive = href === pathname || href === pathname.replace(/\/$/, '');
|
const isActive = href === pathname || href === pathname.replace(/\/$/, '');
|
||||||
---
|
---
|
||||||
|
|
||||||
<a href={href} class:list={[className, { active: isActive }]} {...props}>
|
<a href={href} class:list={[className, { active: isActive }]} {...props}>
|
||||||
<slot />
|
<slot />
|
||||||
</a>
|
</a>
|
||||||
<style>
|
<style>
|
||||||
a {
|
a {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
a.active {
|
a.active {
|
||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,65 +1,65 @@
|
|||||||
---
|
---
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full">
|
<section class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full">
|
||||||
<div class="flex flex-col items-center justify-center gap-y-2 sm:flex-row sm:gap-x-12 sm:gap-y-0 lg:gap-x-24">
|
<div class="flex flex-col items-center justify-center gap-y-2 sm:flex-row sm:gap-x-12 sm:gap-y-0 lg:gap-x-24">
|
||||||
<div class="max-w-5xl px-4 sm:px-6 lg:px-8 mx-auto">
|
<div class="max-w-5xl px-4 sm:px-6 lg:px-8 mx-auto">
|
||||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-6">
|
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-6">
|
||||||
<a class="group flex flex-col bg-neutral-100 dark:bg-neutral-900/30 rounded-xl hover:group" href="#">
|
<a class="group flex flex-col bg-neutral-100 dark:bg-neutral-900/30 rounded-xl hover:group" href="#">
|
||||||
<div class="p-4 md:p-5">
|
<div class="p-4 md:p-5">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<svg class="mt-1 flex-shrink-0 size-5 text-neutral-600 group-hover:text-orange-400 dark:text-neutral-300 dark:group-hover:text-orange-300" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5.8 11.3 2 22l10.7-3.79" /><path d="M4 3h.01" /><path d="M22 8h.01" /><path d="M15 2h.01" /><path d="M22 20h.01" /><path d="m22 2-2.24.75a2.9 2.9 0 0 0-1.96 3.12v0c.1.86-.57 1.63-1.45 1.63h-.38c-.86 0-1.6.6-1.76 1.44L14 10" /><path d="m22 13-.82-.33c-.86-.34-1.82.2-1.98 1.11v0c-.11.7-.72 1.22-1.43 1.22H17" /><path d="m11 2 .33.82c.34.86-.2 1.82-1.11 1.98v0C9.52 4.9 9 5.52 9 6.23V7" /><path d="M11 13c1.93 1.93 2.83 4.17 2 5-.83.83-3.07-.07-5-2-1.93-1.93-2.83-4.17-2-5 .83-.83 3.07.07 5 2Z" /></svg>
|
<svg class="mt-1 flex-shrink-0 size-5 text-neutral-600 group-hover:text-orange-400 dark:text-neutral-300 dark:group-hover:text-orange-300" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5.8 11.3 2 22l10.7-3.79" /><path d="M4 3h.01" /><path d="M22 8h.01" /><path d="M15 2h.01" /><path d="M22 20h.01" /><path d="m22 2-2.24.75a2.9 2.9 0 0 0-1.96 3.12v0c.1.86-.57 1.63-1.45 1.63h-.38c-.86 0-1.6.6-1.76 1.44L14 10" /><path d="m22 13-.82-.33c-.86-.34-1.82.2-1.98 1.11v0c-.11.7-.72 1.22-1.43 1.22H17" /><path d="m11 2 .33.82c.34.86-.2 1.82-1.11 1.98v0C9.52 4.9 9 5.52 9 6.23V7" /><path d="M11 13c1.93 1.93 2.83 4.17 2 5-.83.83-3.07-.07-5-2-1.93-1.93-2.83-4.17-2-5 .83-.83 3.07.07 5 2Z" /></svg>
|
||||||
<div class="grow ms-5">
|
<div class="grow ms-5">
|
||||||
<span class="block text-lg font-bold text-neutral-600 group-hover:text-orange-400 dark:text-neutral-300 dark:group-hover:text-orange-300">Разработка и внедрение системы ХАССП</span>
|
<span class="block text-lg font-bold text-neutral-600 group-hover:text-orange-400 dark:text-neutral-300 dark:group-hover:text-orange-300">Разработка и внедрение системы ХАССП</span>
|
||||||
<span class="mt-1 block text-neutral-500 group-hover:text-neutral-600 dark:text-neutral-500 dark:group-hover:text-neutral-400">
|
<span class="mt-1 block text-neutral-500 group-hover:text-neutral-600 dark:text-neutral-500 dark:group-hover:text-neutral-400">
|
||||||
для одного пищевого объекта (столовой) – 80 000 рублей (без НДС)
|
для одного пищевого объекта (столовой) – 80 000 рублей (без НДС)
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="group flex flex-col bg-neutral-100 dark:bg-neutral-900/30 rounded-xl hover:group" href="#">
|
<a class="group flex flex-col bg-neutral-100 dark:bg-neutral-900/30 rounded-xl hover:group" href="#">
|
||||||
<div class="p-4 md:p-5">
|
<div class="p-4 md:p-5">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<svg class="mt-1 flex-shrink-0 size-5 text-neutral-600 group-hover:text-orange-400 dark:text-neutral-300 dark:group-hover:text-orange-300" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 -.11376601 49.74245785 51.31690859" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m49.626 11.564a.809.809 0 0 1 .028.209v10.972a.8.8 0 0 1 -.402.694l-9.209 5.302v10.509c0 .286-.152.55-.4.694l-19.223 11.066c-.044.025-.092.041-.14.058-.018.006-.035.017-.054.022a.805.805 0 0 1 -.41 0c-.022-.006-.042-.018-.063-.026-.044-.016-.09-.03-.132-.054l-19.219-11.066a.801.801 0 0 1 -.402-.694v-32.916c0-.072.01-.142.028-.21.006-.023.02-.044.028-.067.015-.042.029-.085.051-.124.015-.026.037-.047.055-.071.023-.032.044-.065.071-.093.023-.023.053-.04.079-.06.029-.024.055-.05.088-.069h.001l9.61-5.533a.802.802 0 0 1 .8 0l9.61 5.533h.002c.032.02.059.045.088.068.026.02.055.038.078.06.028.029.048.062.072.094.017.024.04.045.054.071.023.04.036.082.052.124.008.023.022.044.028.068a.809.809 0 0 1 .028.209v20.559l8.008-4.611v-10.51c0-.07.01-.141.028-.208.007-.024.02-.045.028-.068.016-.042.03-.085.052-.124.015-.026.037-.047.054-.071.024-.032.044-.065.072-.093.023-.023.052-.04.078-.06.03-.024.056-.05.088-.069h.001l9.611-5.533a.801.801 0 0 1 .8 0l9.61 5.533c.034.02.06.045.09.068.025.02.054.038.077.06.028.029.048.062.072.094.018.024.04.045.054.071.023.039.036.082.052.124.009.023.022.044.028.068zm-1.574 10.718v-9.124l-3.363 1.936-4.646 2.675v9.124l8.01-4.611zm-9.61 16.505v-9.13l-4.57 2.61-13.05 7.448v9.216zm-36.84-31.068v31.068l17.618 10.143v-9.214l-9.204-5.209-.003-.002-.004-.002c-.031-.018-.057-.044-.086-.066-.025-.02-.054-.036-.076-.058l-.002-.003c-.026-.025-.044-.056-.066-.084-.02-.027-.044-.05-.06-.078l-.001-.003c-.018-.03-.029-.066-.042-.1-.013-.03-.03-.058-.038-.09v-.001c-.01-.038-.012-.078-.016-.117-.004-.03-.012-.06-.012-.09v-21.483l-4.645-2.676-3.363-1.934zm8.81-5.994-8.007 4.609 8.005 4.609 8.006-4.61-8.006-4.608zm4.164 28.764 4.645-2.674v-20.096l-3.363 1.936-4.646 2.675v20.096zm24.667-23.325-8.006 4.609 8.006 4.609 8.005-4.61zm-.801 10.605-4.646-2.675-3.363-1.936v9.124l4.645 2.674 3.364 1.937zm-18.422 20.561 11.743-6.704 5.87-3.35-8-4.606-9.211 5.303-8.395 4.833z" /></svg>
|
<svg class="mt-1 flex-shrink-0 size-5 text-neutral-600 group-hover:text-orange-400 dark:text-neutral-300 dark:group-hover:text-orange-300" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 -.11376601 49.74245785 51.31690859" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m49.626 11.564a.809.809 0 0 1 .028.209v10.972a.8.8 0 0 1 -.402.694l-9.209 5.302v10.509c0 .286-.152.55-.4.694l-19.223 11.066c-.044.025-.092.041-.14.058-.018.006-.035.017-.054.022a.805.805 0 0 1 -.41 0c-.022-.006-.042-.018-.063-.026-.044-.016-.09-.03-.132-.054l-19.219-11.066a.801.801 0 0 1 -.402-.694v-32.916c0-.072.01-.142.028-.21.006-.023.02-.044.028-.067.015-.042.029-.085.051-.124.015-.026.037-.047.055-.071.023-.032.044-.065.071-.093.023-.023.053-.04.079-.06.029-.024.055-.05.088-.069h.001l9.61-5.533a.802.802 0 0 1 .8 0l9.61 5.533h.002c.032.02.059.045.088.068.026.02.055.038.078.06.028.029.048.062.072.094.017.024.04.045.054.071.023.04.036.082.052.124.008.023.022.044.028.068a.809.809 0 0 1 .028.209v20.559l8.008-4.611v-10.51c0-.07.01-.141.028-.208.007-.024.02-.045.028-.068.016-.042.03-.085.052-.124.015-.026.037-.047.054-.071.024-.032.044-.065.072-.093.023-.023.052-.04.078-.06.03-.024.056-.05.088-.069h.001l9.611-5.533a.801.801 0 0 1 .8 0l9.61 5.533c.034.02.06.045.09.068.025.02.054.038.077.06.028.029.048.062.072.094.018.024.04.045.054.071.023.039.036.082.052.124.009.023.022.044.028.068zm-1.574 10.718v-9.124l-3.363 1.936-4.646 2.675v9.124l8.01-4.611zm-9.61 16.505v-9.13l-4.57 2.61-13.05 7.448v9.216zm-36.84-31.068v31.068l17.618 10.143v-9.214l-9.204-5.209-.003-.002-.004-.002c-.031-.018-.057-.044-.086-.066-.025-.02-.054-.036-.076-.058l-.002-.003c-.026-.025-.044-.056-.066-.084-.02-.027-.044-.05-.06-.078l-.001-.003c-.018-.03-.029-.066-.042-.1-.013-.03-.03-.058-.038-.09v-.001c-.01-.038-.012-.078-.016-.117-.004-.03-.012-.06-.012-.09v-21.483l-4.645-2.676-3.363-1.934zm8.81-5.994-8.007 4.609 8.005 4.609 8.006-4.61-8.006-4.608zm4.164 28.764 4.645-2.674v-20.096l-3.363 1.936-4.646 2.675v20.096zm24.667-23.325-8.006 4.609 8.006 4.609 8.005-4.61zm-.801 10.605-4.646-2.675-3.363-1.936v9.124l4.645 2.674 3.364 1.937zm-18.422 20.561 11.743-6.704 5.87-3.35-8-4.606-9.211 5.303-8.395 4.833z" /></svg>
|
||||||
<div class="grow ms-5">
|
<div class="grow ms-5">
|
||||||
<span class="block text-lg font-bold text-neutral-600 group-hover:text-orange-400 dark:text-neutral-300 dark:group-hover:text-orange-300">Срок выполнения работы</span>
|
<span class="block text-lg font-bold text-neutral-600 group-hover:text-orange-400 dark:text-neutral-300 dark:group-hover:text-orange-300">Срок выполнения работы</span>
|
||||||
<span class="mt-1 block text-neutral-500 group-hover:text-neutral-600 dark:text-neutral-500 dark:group-hover:text-neutral-400">
|
<span class="mt-1 block text-neutral-500 group-hover:text-neutral-600 dark:text-neutral-500 dark:group-hover:text-neutral-400">
|
||||||
2 месяца
|
2 месяца
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="group flex flex-col bg-neutral-100 dark:bg-neutral-900/30 rounded-xl hover:group" href="#">
|
<a class="group flex flex-col bg-neutral-100 dark:bg-neutral-900/30 rounded-xl hover:group" href="#">
|
||||||
<div class="p-4 md:p-5">
|
<div class="p-4 md:p-5">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="grow ms-5">
|
<div class="grow ms-5">
|
||||||
<span class="block text-lg font-bold text-neutral-600 group-hover:text-orange-400 dark:text-neutral-300 dark:group-hover:text-orange-300">Обучение сотрудников по работе с документацией системы ХАССП </span>
|
<span class="block text-lg font-bold text-neutral-600 group-hover:text-orange-400 dark:text-neutral-300 dark:group-hover:text-orange-300">Обучение сотрудников по работе с документацией системы ХАССП </span>
|
||||||
<span class="mt-1 block text-neutral-500 group-hover:text-neutral-600 dark:text-neutral-500 dark:group-hover:text-neutral-400">
|
<span class="mt-1 block text-neutral-500 group-hover:text-neutral-600 dark:text-neutral-500 dark:group-hover:text-neutral-400">
|
||||||
(индивидуально или групповые занятия, с выездом или онлайн). Стоимость рассчитывается индивидуально, от 15 000 рублей за одного сотрудника.
|
(индивидуально или групповые занятия, с выездом или онлайн). Стоимость рассчитывается индивидуально, от 15 000 рублей за одного сотрудника.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="group flex flex-col bg-neutral-100 dark:bg-neutral-900/30 rounded-xl hover:group" href="#">
|
<a class="group flex flex-col bg-neutral-100 dark:bg-neutral-900/30 rounded-xl hover:group" href="#">
|
||||||
<div class="p-4 md:p-5">
|
<div class="p-4 md:p-5">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="grow ms-5">
|
<div class="grow ms-5">
|
||||||
<span class="block text-lg font-bold text-neutral-600 group-hover:text-orange-400 dark:text-neutral-300 dark:group-hover:text-orange-300">Документальное подтверждение прохождения обучения</span>
|
<span class="block text-lg font-bold text-neutral-600 group-hover:text-orange-400 dark:text-neutral-300 dark:group-hover:text-orange-300">Документальное подтверждение прохождения обучения</span>
|
||||||
<span class="mt-1 block text-neutral-500 group-hover:text-neutral-600 dark:text-neutral-500 dark:group-hover:text-neutral-400">
|
<span class="mt-1 block text-neutral-500 group-hover:text-neutral-600 dark:text-neutral-500 dark:group-hover:text-neutral-400">
|
||||||
на внутренних аудиторов системы ХАССП.
|
на внутренних аудиторов системы ХАССП.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,60 +1,60 @@
|
|||||||
---
|
---
|
||||||
import { Image } from 'astro:assets'
|
import { Image } from 'astro:assets'
|
||||||
import PrimaryCTA from '@components/buttons/PrimaryCTA.astro'
|
import PrimaryCTA from '@components/buttons/PrimaryCTA.astro'
|
||||||
import SecondaryCTA from '@components/buttons/SecondaryCTA.astro'
|
import SecondaryCTA from '@components/buttons/SecondaryCTA.astro'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
subTitle,
|
subTitle,
|
||||||
primaryBtn,
|
primaryBtn,
|
||||||
primaryBtnURL,
|
primaryBtnURL,
|
||||||
secondaryBtn,
|
secondaryBtn,
|
||||||
secondaryBtnURL,
|
secondaryBtnURL,
|
||||||
src,
|
src,
|
||||||
alt,
|
alt,
|
||||||
} = Astro.props
|
} = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
subTitle?: string
|
subTitle?: string
|
||||||
primaryBtn?: string
|
primaryBtn?: string
|
||||||
primaryBtnURL?: string
|
primaryBtnURL?: string
|
||||||
secondaryBtn?: string
|
secondaryBtn?: string
|
||||||
secondaryBtnURL?: string
|
secondaryBtnURL?: string
|
||||||
src?: any
|
src?: any
|
||||||
alt?: string
|
alt?: string
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="mx-auto grid max-w-[85rem] gap-4 px-4 py-14 sm:px-6 md:grid-cols-2 md:items-center md:gap-8 lg:px-8 2xl:max-w-full">
|
<section class="mx-auto grid max-w-[85rem] gap-4 px-4 py-14 sm:px-6 md:grid-cols-2 md:items-center md:gap-8 lg:px-8 2xl:max-w-full">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="block text-balance text-3xl font-bold tracking-tight text-neutral-800 dark:text-neutral-200 sm:text-4xl lg:text-6xl lg:leading-tight">
|
<h1 class="block text-balance text-3xl font-bold tracking-tight text-neutral-800 dark:text-neutral-200 sm:text-4xl lg:text-6xl lg:leading-tight">
|
||||||
<Fragment set:html={title} />
|
<Fragment set:html={title} />
|
||||||
</h1>
|
</h1>
|
||||||
{subTitle && (
|
{subTitle && (
|
||||||
<p class="mt-6 text-pretty text-lg leading-relaxed text-neutral-700 dark:text-neutral-400 lg:w-4/5">
|
<p class="mt-6 text-pretty text-lg leading-relaxed text-neutral-700 dark:text-neutral-400 lg:w-4/5">
|
||||||
{subTitle}
|
{subTitle}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div class="mt-7 grid w-full gap-3 sm:inline-flex">
|
<div class="mt-7 grid w-full gap-3 sm:inline-flex">
|
||||||
{primaryBtn && <PrimaryCTA title={primaryBtn} url={primaryBtnURL} />}
|
{primaryBtn && <PrimaryCTA title={primaryBtn} url={primaryBtnURL} />}
|
||||||
{secondaryBtn && <SecondaryCTA title={secondaryBtn} url={secondaryBtnURL} />}
|
{secondaryBtn && <SecondaryCTA title={secondaryBtn} url={secondaryBtnURL} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full hidden md:block">
|
<div class="w-full hidden md:block">
|
||||||
<div class="top-12 overflow-hidden w-full md:ml-4 flex justify-center">
|
<div class="top-12 overflow-hidden w-full md:ml-4 flex justify-center">
|
||||||
{src && alt && (
|
{src && alt && (
|
||||||
<Image
|
<Image
|
||||||
src={src}
|
src={src}
|
||||||
alt={alt}
|
alt={alt}
|
||||||
class="h-full w-[320px] scale-100 object-cover object-center"
|
class="h-full w-[320px] scale-100 object-cover object-center"
|
||||||
draggable="false"
|
draggable="false"
|
||||||
loading="eager"
|
loading="eager"
|
||||||
format="avif"
|
format="avif"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,140 +1,140 @@
|
|||||||
---
|
---
|
||||||
import GithubBtn from '@components/buttons/GithubBtn.astro'
|
import GithubBtn from '@components/buttons/GithubBtn.astro'
|
||||||
|
|
||||||
const { title, subTitle, url } = Astro.props
|
const { title, subTitle, url } = Astro.props
|
||||||
|
|
||||||
const btnTitle = 'Телефон'
|
const btnTitle = 'Телефон'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
subTitle?: string
|
subTitle?: string
|
||||||
url?: string
|
url?: string
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="relative mx-auto max-w-[85rem] px-4 pb-24 pt-10 sm:px-6 lg:px-8">
|
<section class="relative mx-auto max-w-[85rem] px-4 pb-24 pt-10 sm:px-6 lg:px-8">
|
||||||
<div class="absolute left-0 top-[55%] scale-90 md:top-[20%] xl:left-[10%] xl:top-[25%]">
|
<div class="absolute left-0 top-[55%] scale-90 md:top-[20%] xl:left-[10%] xl:top-[25%]">
|
||||||
<svg
|
<svg
|
||||||
width="64"
|
width="64"
|
||||||
height="64"
|
height="64"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
color="#ea580c"
|
color="#ea580c"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
fill="#ea580c"
|
fill="#ea580c"
|
||||||
stroke="#ea580c"
|
stroke="#ea580c"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
d="M12 23a1 1 0 1 0 0-2 1 1 0 0 0 0 2ZM3 8a1 1 0 1 0 0-2 1 1 0 0 0 0 2ZM3 18a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
|
d="M12 23a1 1 0 1 0 0-2 1 1 0 0 0 0 2ZM3 8a1 1 0 1 0 0-2 1 1 0 0 0 0 2ZM3 18a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
|
||||||
></path>
|
></path>
|
||||||
<path
|
<path
|
||||||
stroke="#ea580c"
|
stroke="#ea580c"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
d="M21 7.353v9.294a.6.6 0 0 1-.309.525l-8.4 4.666a.6.6 0 0 1-.582 0l-8.4-4.666A.6.6 0 0 1 3 16.647V7.353a.6.6 0 0 1 .309-.524l8.4-4.667a.6.6 0 0 1 .582 0l8.4 4.667a.6.6 0 0 1 .309.524Z"
|
d="M21 7.353v9.294a.6.6 0 0 1-.309.525l-8.4 4.666a.6.6 0 0 1-.582 0l-8.4-4.666A.6.6 0 0 1 3 16.647V7.353a.6.6 0 0 1 .309-.524l8.4-4.667a.6.6 0 0 1 .582 0l8.4 4.667a.6.6 0 0 1 .309.524Z"
|
||||||
></path>
|
></path>
|
||||||
<path
|
<path
|
||||||
stroke="#ea580c"
|
stroke="#ea580c"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
d="m3.528 7.294 8.18 4.544a.6.6 0 0 0 .583 0l8.209-4.56M12 21v-9"
|
d="m3.528 7.294 8.18 4.544a.6.6 0 0 0 .583 0l8.209-4.56M12 21v-9"
|
||||||
></path>
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="absolute left-[85%] top-0 scale-75">
|
<div class="absolute left-[85%] top-0 scale-75">
|
||||||
<svg
|
<svg
|
||||||
width="64"
|
width="64"
|
||||||
height="64"
|
height="64"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
color="#fbbf24"
|
color="#fbbf24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
stroke="#fbbf24"
|
stroke="#fbbf24"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Z"
|
d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Z"
|
||||||
></path>
|
></path>
|
||||||
<path
|
<path
|
||||||
fill="#fbbf24"
|
fill="#fbbf24"
|
||||||
stroke="#fbbf24"
|
stroke="#fbbf24"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
d="M5 6a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
|
d="M5 6a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
|
||||||
></path>
|
></path>
|
||||||
<path
|
<path
|
||||||
stroke="#fbbf24"
|
stroke="#fbbf24"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
d="M5 10.5V9M5 15v-1.5"
|
d="M5 10.5V9M5 15v-1.5"
|
||||||
></path>
|
></path>
|
||||||
<path
|
<path
|
||||||
fill="#fbbf24"
|
fill="#fbbf24"
|
||||||
stroke="#fbbf24"
|
stroke="#fbbf24"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
d="M5 20a1 1 0 1 0 0-2 1 1 0 0 0 0 2ZM19 20a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
|
d="M5 20a1 1 0 1 0 0-2 1 1 0 0 0 0 2ZM19 20a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
|
||||||
></path>
|
></path>
|
||||||
<path
|
<path
|
||||||
stroke="#fbbf24"
|
stroke="#fbbf24"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
d="M10.5 19H9M15 19h-1.5"
|
d="M10.5 19H9M15 19h-1.5"
|
||||||
></path>
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="absolute bottom-[5%] left-[60%] scale-[.6] xl:bottom-[15%] xl:left-[35%]"
|
class="absolute bottom-[5%] left-[60%] scale-[.6] xl:bottom-[15%] xl:left-[35%]"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
width="64"
|
width="64"
|
||||||
height="64"
|
height="64"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
color="#a3a3a3"
|
color="#a3a3a3"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
stroke="#a3a3a3"
|
stroke="#a3a3a3"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
d="M5.164 17c.29-1.049.67-2.052 1.132-3M11.5 7.794A16.838 16.838 0 0 1 14 6.296M4.5 22a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5Z"
|
d="M5.164 17c.29-1.049.67-2.052 1.132-3M11.5 7.794A16.838 16.838 0 0 1 14 6.296M4.5 22a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5Z"
|
||||||
></path>
|
></path>
|
||||||
<path
|
<path
|
||||||
stroke="#a3a3a3"
|
stroke="#a3a3a3"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
d="M9.5 12a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5ZM19.5 7a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5Z"
|
d="M9.5 12a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5ZM19.5 7a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5Z"
|
||||||
></path>
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<!-- Hero Section Heading -->
|
<!-- Hero Section Heading -->
|
||||||
<div class="mx-auto mt-5 max-w-xl text-center">
|
<div class="mx-auto mt-5 max-w-xl text-center">
|
||||||
<h2
|
<h2
|
||||||
class="block text-balance text-4xl font-bold leading-tight tracking-tight text-neutral-800 dark:text-neutral-200 md:text-5xl lg:text-6xl"
|
class="block text-balance text-4xl font-bold leading-tight tracking-tight text-neutral-800 dark:text-neutral-200 md:text-5xl lg:text-6xl"
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<!-- Hero Section Sub-heading -->
|
<!-- Hero Section Sub-heading -->
|
||||||
<div class="mx-auto mt-5 max-w-3xl text-center">
|
<div class="mx-auto mt-5 max-w-3xl text-center">
|
||||||
{
|
{
|
||||||
subTitle && (
|
subTitle && (
|
||||||
<p class="text-pretty text-lg text-neutral-600 dark:text-neutral-400">
|
<p class="text-pretty text-lg text-neutral-600 dark:text-neutral-400">
|
||||||
{subTitle}
|
{subTitle}
|
||||||
</p>
|
</p>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<!-- Github Button -->
|
<!-- Github Button -->
|
||||||
{
|
{
|
||||||
url && (
|
url && (
|
||||||
<div class="mt-8 flex justify-center gap-3">
|
<div class="mt-8 flex justify-center gap-3">
|
||||||
<GithubBtn url={url} title={btnTitle} />
|
<GithubBtn url={url} title={btnTitle} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
---
|
---
|
||||||
import PrimaryCTA from '@components/buttons/PrimaryCTA.astro'
|
import PrimaryCTA from '@components/buttons/PrimaryCTA.astro'
|
||||||
|
|
||||||
const { title, subTitle, btnExists, btnTitle, btnURL } = Astro.props
|
const { title, subTitle, btnExists, btnTitle, btnURL } = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
subTitle: string
|
subTitle: string
|
||||||
btnExists?: boolean
|
btnExists?: boolean
|
||||||
btnTitle?: string
|
btnTitle?: string
|
||||||
btnURL?: string
|
btnURL?: string
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
<section class="mx-auto mt-10 px-4 sm:px-6 lg:px-8 lg:pt-10 2xl:max-w-full">
|
<section class="mx-auto mt-10 px-4 sm:px-6 lg:px-8 lg:pt-10 2xl:max-w-full">
|
||||||
<div class="md:flex md:justify-between md:items-center flex-wrap">
|
<div class="md:flex md:justify-between md:items-center flex-wrap">
|
||||||
<div class="w-full md:w-auto">
|
<div class="w-full md:w-auto">
|
||||||
<h1 class="block text-balance text-4xl font-bold tracking-tight text-neutral-800 dark:text-neutral-200 md:text-5xl lg:text-6xl">
|
<h1 class="block text-balance text-4xl font-bold tracking-tight text-neutral-800 dark:text-neutral-200 md:text-5xl lg:text-6xl">
|
||||||
{title}
|
{title}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="mt-4 text-pretty text-lg text-neutral-600 dark:text-neutral-400">
|
<p class="mt-4 text-pretty text-lg text-neutral-600 dark:text-neutral-400">
|
||||||
{subTitle}
|
{subTitle}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
btnExists
|
btnExists
|
||||||
? (
|
? (
|
||||||
<div class="md:w-auto mt-4 md:mt-0">
|
<div class="md:w-auto mt-4 md:mt-0">
|
||||||
<PrimaryCTA title={btnTitle} url={btnURL} />
|
<PrimaryCTA title={btnTitle} url={btnURL} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
---
|
---
|
||||||
import type { CollectionEntry } from 'astro:content'
|
import type { CollectionEntry } from 'astro:content'
|
||||||
import { Image } from 'astro:assets'
|
import { Image } from 'astro:assets'
|
||||||
import { formatDate } from '../../support/time'
|
import { formatDate } from '../../support/time'
|
||||||
|
|
||||||
const { blog } = Astro.props
|
const { blog } = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
blog: CollectionEntry<'posts'>
|
blog: CollectionEntry<'posts'>
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<a
|
<a
|
||||||
class="group relative block rounded-xl outline-none ring-zinc-500 transition duration-500 focus-visible:ring dark:ring-zinc-200 dark:focus:outline-none"
|
class="group relative block rounded-xl outline-none ring-zinc-500 transition duration-500 focus-visible:ring dark:ring-zinc-200 dark:focus:outline-none"
|
||||||
href={`/posts/${blog.slug}/`}
|
href={`/posts/${blog.slug}/`}
|
||||||
data-astro-prefetch
|
data-astro-prefetch
|
||||||
>
|
>
|
||||||
<div class="relative w-full flex-shrink-0 overflow-hidden rounded-xl before:absolute before:inset-x-0 before:z-[1] before:size-full before:bg-gradient-to-t before:from-neutral-900/[.7]">
|
<div class="relative w-full flex-shrink-0 overflow-hidden rounded-xl before:absolute before:inset-x-0 before:z-[1] before:size-full before:bg-gradient-to-t before:from-neutral-900/[.7]">
|
||||||
<Image
|
<Image
|
||||||
class="w-full h-full sm:h-[220px] md:h-[240px] start-0 top-0 size-full object-cover transition duration-500 group-hover:scale-110"
|
class="w-full h-full sm:h-[220px] md:h-[240px] start-0 top-0 size-full object-cover transition duration-500 group-hover:scale-110"
|
||||||
src={blog.data.banner}
|
src={blog.data.banner}
|
||||||
alt={blog.data.title}
|
alt={blog.data.title}
|
||||||
draggable="false"
|
draggable="false"
|
||||||
loading="eager"
|
loading="eager"
|
||||||
format="avif"
|
format="avif"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="mt-2 text-xl font-bold text-neutral-800 group-hover:text-orange-400 dark:text-neutral-200 dark:group-hover:text-orange-300">
|
<h3 class="mt-2 text-xl font-bold text-neutral-800 group-hover:text-orange-400 dark:text-neutral-200 dark:group-hover:text-orange-300">
|
||||||
{blog.data.title}
|
{blog.data.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p class="mt-2 text-sm text-gray-600 dark:text-neutral-400 dark:group-hover:text-neutral-500">
|
<p class="mt-2 text-sm text-gray-600 dark:text-neutral-400 dark:group-hover:text-neutral-500">
|
||||||
{formatDate(blog.data.pubDate)}
|
{formatDate(blog.data.pubDate)}
|
||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,66 +1,66 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
const { slug, title, description, count, publishDate } = Astro.props
|
const { slug, title, description, count, publishDate } = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
slug: string
|
slug: string
|
||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
count: number
|
count: number
|
||||||
publishDate: string
|
publishDate: string
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<a href=`/categories/${slug}` class="flex flex-col justify-between size-full bg-neutral-100 shadow-lg rounded-lg p-5 dark:bg-neutral-900/30 group">
|
<a href=`/categories/${slug}` class="flex flex-col justify-between size-full bg-neutral-100 shadow-lg rounded-lg p-5 dark:bg-neutral-900/30 group">
|
||||||
<div class="flex justify-between items-center mb-3">
|
<div class="flex justify-between items-center mb-3">
|
||||||
<div class="flex justify-center gap-x-4 items-center">
|
<div class="flex justify-center gap-x-4 items-center">
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<h3 class="text-lg font-semibold tracking-tight text-neutral-600 group-hover:text-orange-400 dark:text-neutral-300 dark:group-hover:text-orange-300">
|
<h3 class="text-lg font-semibold tracking-tight text-neutral-600 group-hover:text-orange-400 dark:text-neutral-300 dark:group-hover:text-orange-300">
|
||||||
{title}
|
{title}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="block text-neutral-500 min-h-[60px] group-hover:text-neutral-600 dark:text-neutral-500 dark:group-hover:text-neutral-400">
|
<p class="block text-neutral-500 min-h-[60px] group-hover:text-neutral-600 dark:text-neutral-500 dark:group-hover:text-neutral-400">
|
||||||
{description}
|
{description}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="flex mt-2 mb-0 gap-x-5">
|
<div class="flex mt-2 mb-0 gap-x-5">
|
||||||
<span class="text-xs inline-flex items-center rounded me-2 text-neutral-500 group-hover:text-neutral-600 dark:text-neutral-500 dark:group-hover:text-neutral-400">
|
<span class="text-xs inline-flex items-center rounded me-2 text-neutral-500 group-hover:text-neutral-600 dark:text-neutral-500 dark:group-hover:text-neutral-400">
|
||||||
<svg
|
<svg
|
||||||
class="w-3 h-3 me-1.5"
|
class="w-3 h-3 me-1.5"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<rect width="8" height="4" x="8" y="2" rx="1" />
|
<rect width="8" height="4" x="8" y="2" rx="1" />
|
||||||
<path d="M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-.5" />
|
<path d="M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-.5" />
|
||||||
<path d="M16 4h2a2 2 0 0 1 1.73 1" />
|
<path d="M16 4h2a2 2 0 0 1 1.73 1" />
|
||||||
<path d="M8 18h1" />
|
<path d="M8 18h1" />
|
||||||
<path d="M18.4 9.6a2 2 0 0 1 3 3L17 17l-4 1 1-4Z" />
|
<path d="M18.4 9.6a2 2 0 0 1 3 3L17 17l-4 1 1-4Z" />
|
||||||
</svg>
|
</svg>
|
||||||
{count} posts
|
{count} posts
|
||||||
</span>
|
</span>
|
||||||
<span class="text-xs inline-flex items-center rounded me-2 text-neutral-500 group-hover:text-neutral-600 dark:text-neutral-500 dark:group-hover:text-neutral-400">
|
<span class="text-xs inline-flex items-center rounded me-2 text-neutral-500 group-hover:text-neutral-600 dark:text-neutral-500 dark:group-hover:text-neutral-400">
|
||||||
<svg
|
<svg
|
||||||
class="w-3 h-3 me-1.5"
|
class="w-3 h-3 me-1.5"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<circle cx="12" cy="12" r="10" />
|
<circle cx="12" cy="12" r="10" />
|
||||||
<polyline points="12 6 12 12 16 14" />
|
<polyline points="12 6 12 12 16 14" />
|
||||||
</svg>
|
</svg>
|
||||||
update {publishDate}
|
update {publishDate}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,62 +1,62 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import Giscus from '@giscus/react'
|
import Giscus from '@giscus/react'
|
||||||
|
|
||||||
const id = 'inject-comments'
|
const id = 'inject-comments'
|
||||||
|
|
||||||
function getCurrentTheme(): string {
|
function getCurrentTheme(): string {
|
||||||
if (window.localStorage.getItem('hs_theme')) {
|
if (window.localStorage.getItem('hs_theme')) {
|
||||||
return window.localStorage.getItem('hs_theme') ?? 'default'
|
return window.localStorage.getItem('hs_theme') ?? 'default'
|
||||||
}
|
}
|
||||||
|
|
||||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'default'
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'default'
|
||||||
}
|
}
|
||||||
|
|
||||||
function BlogComments() {
|
function BlogComments() {
|
||||||
const [mounted, setMounted] = React.useState(false)
|
const [mounted, setMounted] = React.useState(false)
|
||||||
const [theme, setTheme] = React.useState('dark')
|
const [theme, setTheme] = React.useState('dark')
|
||||||
|
|
||||||
const handleThemeChange = (event: any) => {
|
const handleThemeChange = (event: any) => {
|
||||||
const theme = event?.detail ?? 'light'
|
const theme = event?.detail ?? 'light'
|
||||||
setTheme(theme)
|
setTheme(theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const theme = getCurrentTheme()
|
const theme = getCurrentTheme()
|
||||||
setTheme(theme)
|
setTheme(theme)
|
||||||
|
|
||||||
window.addEventListener('on-hs-appearance-change', handleThemeChange)
|
window.addEventListener('on-hs-appearance-change', handleThemeChange)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('on-hs-appearance-change', handleThemeChange)
|
window.removeEventListener('on-hs-appearance-change', handleThemeChange)
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setMounted(true)
|
setMounted(true)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id={id} className="w-full">
|
<div id={id} className="w-full">
|
||||||
{mounted
|
{mounted
|
||||||
? (
|
? (
|
||||||
<Giscus
|
<Giscus
|
||||||
id={id}
|
id={id}
|
||||||
repo="godruoyi/gblog"
|
repo="godruoyi/gblog"
|
||||||
repoId="MDEwOlJlcG9zaXRvcnkxMjcyODI0NzA"
|
repoId="MDEwOlJlcG9zaXRvcnkxMjcyODI0NzA"
|
||||||
category="Announcements"
|
category="Announcements"
|
||||||
categoryId="DIC_kwDOB5YtJs4CfZnX"
|
categoryId="DIC_kwDOB5YtJs4CfZnX"
|
||||||
mapping="title"
|
mapping="title"
|
||||||
reactionsEnabled="1"
|
reactionsEnabled="1"
|
||||||
emitMetadata="0"
|
emitMetadata="0"
|
||||||
inputPosition="top"
|
inputPosition="top"
|
||||||
lang="zh-CN"
|
lang="zh-CN"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BlogComments
|
export default BlogComments
|
||||||
|
|||||||
@@ -1,44 +1,44 @@
|
|||||||
---
|
---
|
||||||
import Icon from '@components/icons/icon.astro'
|
import Icon from '@components/icons/icon.astro'
|
||||||
import { Image } from 'astro:assets'
|
import { Image } from 'astro:assets'
|
||||||
import type { CollectionEntry } from 'astro:content'
|
import type { CollectionEntry } from 'astro:content'
|
||||||
|
|
||||||
const { blog } = Astro.props
|
const { blog } = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
blog: CollectionEntry<'posts'>
|
blog: CollectionEntry<'posts'>
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<a
|
<a
|
||||||
class="group outline-none rounded-xl ring-zinc-500 transition duration-300 focus-visible:ring dark:ring-zinc-200 dark:focus:outline-none"
|
class="group outline-none rounded-xl ring-zinc-500 transition duration-300 focus-visible:ring dark:ring-zinc-200 dark:focus:outline-none"
|
||||||
href={`/posts/${blog.slug}/`}
|
href={`/posts/${blog.slug}/`}
|
||||||
>
|
>
|
||||||
<div class="aspect-w-16 aspect-h-10 relative overflow-hidden rounded-xl ">
|
<div class="aspect-w-16 aspect-h-10 relative overflow-hidden rounded-xl ">
|
||||||
<Image
|
<Image
|
||||||
class="w-full h-full sm:h-[220px] md:h-[240px] object-cover rounded-xl transition duration-500 ease-in-out group-hover:scale-105"
|
class="w-full h-full sm:h-[220px] md:h-[240px] object-cover rounded-xl transition duration-500 ease-in-out group-hover:scale-105"
|
||||||
src={blog.data.banner}
|
src={blog.data.banner}
|
||||||
alt={blog.data.title}
|
alt={blog.data.title}
|
||||||
format="avif"
|
format="avif"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-7">
|
<div class="mt-7">
|
||||||
<h3
|
<h3
|
||||||
class="text-xl font-bold text-neutral-800 group-hover:text-neutral-600 dark:text-neutral-200 dark:group-hover:text-neutral-400"
|
class="text-xl font-bold text-neutral-800 group-hover:text-neutral-600 dark:text-neutral-200 dark:group-hover:text-neutral-400"
|
||||||
>
|
>
|
||||||
{blog.data.title}
|
{blog.data.title}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p class="mt-3 text-neutral-600 dark:text-neutral-400">
|
<p class="mt-3 text-neutral-600 dark:text-neutral-400">
|
||||||
{blog.data.description}
|
{blog.data.description}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p
|
<p
|
||||||
class="mt-5 inline-flex items-center gap-x-1 font-medium text-orange-400 decoration-2 group-hover:underline dark:text-orange-300"
|
class="mt-5 inline-flex items-center gap-x-1 font-medium text-orange-400 decoration-2 group-hover:underline dark:text-orange-300"
|
||||||
>
|
>
|
||||||
Подробнее <Icon name="arrowRightStatic" />
|
Подробнее <Icon name="arrowRightStatic" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,44 +1,44 @@
|
|||||||
---
|
---
|
||||||
import type { CollectionEntry } from 'astro:content'
|
import type { CollectionEntry } from 'astro:content'
|
||||||
import LeftSection from './blocks/LeftSection.astro'
|
import LeftSection from './blocks/LeftSection.astro'
|
||||||
import RightSection from './blocks/RightSection.astro'
|
import RightSection from './blocks/RightSection.astro'
|
||||||
|
|
||||||
const { blogs } = Astro.props
|
const { blogs } = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
blogs: CollectionEntry<'posts'>[]
|
blogs: CollectionEntry<'posts'>[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const posts = blogs.slice(0, 5)
|
const posts = blogs.slice(0, 5)
|
||||||
---
|
---
|
||||||
|
|
||||||
{
|
{
|
||||||
posts.map((b, index) => index % 2 === 0
|
posts.map((b, index) => index % 2 === 0
|
||||||
? (
|
? (
|
||||||
<LeftSection
|
<LeftSection
|
||||||
title={b.data.title}
|
title={b.data.title}
|
||||||
subTitle={b.data.description}
|
subTitle={b.data.description}
|
||||||
btnExists={true}
|
btnExists={true}
|
||||||
btnTitle="Подробнее"
|
btnTitle="Подробнее"
|
||||||
btnURL=`/posts/${b.slug}`
|
btnURL=`/posts/${b.slug}`
|
||||||
img={b.data.banner}
|
img={b.data.banner}
|
||||||
imgAlt={b.data.title}
|
imgAlt={b.data.title}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
<RightSection
|
<RightSection
|
||||||
title={b.data.title}
|
title={b.data.title}
|
||||||
subTitle={b.data.description}
|
subTitle={b.data.description}
|
||||||
btnExists={true}
|
btnExists={true}
|
||||||
btnTitle="Подробнее"
|
btnTitle="Подробнее"
|
||||||
btnURL=`/posts/${b.slug}`
|
btnURL=`/posts/${b.slug}`
|
||||||
img={b.data.banner}
|
img={b.data.banner}
|
||||||
imgAlt={b.data.title}
|
imgAlt={b.data.title}
|
||||||
single={!b.data.banner2}
|
single={!b.data.banner2}
|
||||||
imgOne={b.data.banner}
|
imgOne={b.data.banner}
|
||||||
imgOneAlt={b.data.title}
|
imgOneAlt={b.data.title}
|
||||||
imgTwo={b.data?.banner2}
|
imgTwo={b.data?.banner2}
|
||||||
imgTwoAlt={b.data.title}
|
imgTwoAlt={b.data.title}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
---
|
---
|
||||||
import type { CollectionEntry } from 'astro:content'
|
import type { CollectionEntry } from 'astro:content'
|
||||||
import Icon from '../icons/icon.astro'
|
import Icon from '../icons/icon.astro'
|
||||||
import BlogCard from './BlogCard.astro'
|
import BlogCard from './BlogCard.astro'
|
||||||
|
|
||||||
const { posts } = Astro.props
|
const { posts } = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
posts: CollectionEntry<'posts'>[]
|
posts: CollectionEntry<'posts'>[]
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="mx-auto max-w-[85rem] px-4 py-8 sm:px-6 lg:px-8 mb-10 2xl:max-w-full">
|
<section class="mx-auto max-w-[85rem] px-4 py-8 sm:px-6 lg:px-8 mb-10 2xl:max-w-full">
|
||||||
<div class="text-left">
|
<div class="text-left">
|
||||||
<h2 id="selected-articel" class="mb-4 text-balance text-5xl font-extrabold tracking-tight text-neutral-800 dark:text-neutral-200">
|
<h2 id="selected-articel" class="mb-4 text-balance text-5xl font-extrabold tracking-tight text-neutral-800 dark:text-neutral-200">
|
||||||
Выбрать статью
|
Выбрать статью
|
||||||
</h2>
|
</h2>
|
||||||
<p class="mb-8 max-w-prose text-pretty font-light text-neutral-600 dark:text-neutral-400 sm:text-xl">
|
<p class="mb-8 max-w-prose text-pretty font-light text-neutral-600 dark:text-neutral-400 sm:text-xl">
|
||||||
У нас полно историй от начала комарческой деятельности до сегодняшних дней.
|
У нас полно историй от начала комарческой деятельности до сегодняшних дней.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid gap-6 grid-cols-1 lg:grid-cols-3 sm:grid-cols-2">
|
<div class="grid gap-6 grid-cols-1 lg:grid-cols-3 sm:grid-cols-2">
|
||||||
{posts.map(b => <BlogCard blog={b} />)}
|
{posts.map(b => <BlogCard blog={b} />)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center mt-6">
|
<div class="text-center mt-6">
|
||||||
<a
|
<a
|
||||||
href="/timeline"
|
href="/timeline"
|
||||||
class="group inline-flex items-center justify-center gap-x-2 rounded-full px-4 py-3 text-sm font-bold text-neutral-50 ring-zinc-500 transition duration-300 focus-visible:ring outline-none border border-transparent bg-orange-400 hover:bg-orange-500 active:bg-orange-500 dark:focus:outline-none disabled:pointer-events-none disabled:opacity-50 2xl:text-base dark:ring-zinc-200"
|
class="group inline-flex items-center justify-center gap-x-2 rounded-full px-4 py-3 text-sm font-bold text-neutral-50 ring-zinc-500 transition duration-300 focus-visible:ring outline-none border border-transparent bg-orange-400 hover:bg-orange-500 active:bg-orange-500 dark:focus:outline-none disabled:pointer-events-none disabled:opacity-50 2xl:text-base dark:ring-zinc-200"
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-x-2">
|
<div class="flex items-center gap-x-2">
|
||||||
<p>Посмотреть хронологию событий</p>
|
<p>Посмотреть хронологию событий</p>
|
||||||
<Icon name="arrowRight" class="h-4 w-4 flex-shrink-0 transition duration-300 group-hover:translate-x-1" />
|
<Icon name="arrowRight" class="h-4 w-4 flex-shrink-0 transition duration-300 group-hover:translate-x-1" />
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
---
|
---
|
||||||
import type { CollectionEntry } from 'astro:content'
|
import type { CollectionEntry } from 'astro:content'
|
||||||
|
|
||||||
const { blog } = Astro.props
|
const { blog } = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
blog: CollectionEntry<'posts'>
|
blog: CollectionEntry<'posts'>
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="max-w-3xl mx-auto">
|
<div class="max-w-3xl mx-auto">
|
||||||
<div class="flex gap-x-2">
|
<div class="flex gap-x-2">
|
||||||
<div class="relative -ml-2 last:after:hidden after:absolute after:top-7 after:bottom-0 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200 dark:after:bg-neutral-700 after:bg-neutral-400">
|
<div class="relative -ml-2 last:after:hidden after:absolute after:top-7 after:bottom-0 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200 dark:after:bg-neutral-700 after:bg-neutral-400">
|
||||||
<div class="relative z-10 size-7 flex justify-center items-center">
|
<div class="relative z-10 size-7 flex justify-center items-center">
|
||||||
<div class="size-2 rounded-full bg-neutral-400 dark:bg-neutral-600"></div>
|
<div class="size-2 rounded-full bg-neutral-400 dark:bg-neutral-600"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grow pt-0.5 pb-8 group">
|
<div class="grow pt-0.5 pb-8 group">
|
||||||
<a
|
<a
|
||||||
href=`/posts/${blog.slug}`
|
href=`/posts/${blog.slug}`
|
||||||
data-astro-prefetch
|
data-astro-prefetch
|
||||||
class="flex justify-between text-base font-bold text-neutral-700 outline-none ring-zinc-500 focus:outline-none focus-visible:ring focus-visible:ring-zinc-500 dark:ring-zinc-200"
|
class="flex justify-between text-base font-bold text-neutral-700 outline-none ring-zinc-500 focus:outline-none focus-visible:ring focus-visible:ring-zinc-500 dark:ring-zinc-200"
|
||||||
>
|
>
|
||||||
<h3 class="flex gap-x-1.5 text-xl font-bold text-neutral-800 group-hover:text-orange-600 dark:text-neutral-200 dark:group-hover:text-orange-300">{blog.data.title}</h3>
|
<h3 class="flex gap-x-1.5 text-xl font-bold text-neutral-800 group-hover:text-orange-600 dark:text-neutral-200 dark:group-hover:text-orange-300">{blog.data.title}</h3>
|
||||||
<!--<span class="tracking-wide text-xs font-medium text-neutral-600 dark:text-neutral-500 group-hover:text-neutral-600 dark:group-hover:text-neutral-400">{formatDate(blog.data.pubDate)}</span>-->
|
<!--<span class="tracking-wide text-xs font-medium text-neutral-600 dark:text-neutral-500 group-hover:text-neutral-600 dark:group-hover:text-neutral-400">{formatDate(blog.data.pubDate)}</span>-->
|
||||||
</a>
|
</a>
|
||||||
<p class="mt-1.5 text-base text-neutral-600 group-hover:text-neutral-500 dark:text-neutral-500 dark:group-hover:text-neutral-400">{blog.data.description}</p>
|
<p class="mt-1.5 text-base text-neutral-600 group-hover:text-neutral-500 dark:text-neutral-500 dark:group-hover:text-neutral-400">{blog.data.description}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,40 +1,40 @@
|
|||||||
---
|
---
|
||||||
import PrimaryCTA from '@components/buttons/PrimaryCTA.astro'
|
import PrimaryCTA from '@components/buttons/PrimaryCTA.astro'
|
||||||
import { Image } from 'astro:assets'
|
import { Image } from 'astro:assets'
|
||||||
|
|
||||||
const { title, subTitle, btnExists, btnTitle, btnURL, img, imgAlt } = Astro.props
|
const { title, subTitle, btnExists, btnTitle, btnURL, img, imgAlt } = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
subTitle: string
|
subTitle: string
|
||||||
btnExists?: boolean
|
btnExists?: boolean
|
||||||
btnTitle?: string
|
btnTitle?: string
|
||||||
btnURL?: string
|
btnURL?: string
|
||||||
img: any
|
img: any
|
||||||
imgAlt: any
|
imgAlt: any
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<section
|
<section
|
||||||
class="mx-auto max-w-[85rem] items-center gap-8 px-4 py-10 sm:px-6 sm:py-16 md:grid md:grid-cols-2 lg:grid lg:grid-cols-2 lg:px-8 lg:py-14 xl:gap-16 2xl:max-w-full"
|
class="mx-auto max-w-[85rem] items-center gap-8 px-4 py-10 sm:px-6 sm:py-16 md:grid md:grid-cols-2 lg:grid lg:grid-cols-2 lg:px-8 lg:py-14 xl:gap-16 2xl:max-w-full"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
class="w-full rounded-xl h-full sm:max-h-[320px] md:max-h-[360px] object-cover"
|
class="w-full rounded-xl h-full sm:max-h-[320px] md:max-h-[360px] object-cover"
|
||||||
src={img}
|
src={img}
|
||||||
alt={imgAlt}
|
alt={imgAlt}
|
||||||
draggable="false"
|
draggable="false"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="mt-4 md:mt-0">
|
<div class="mt-4 md:mt-0">
|
||||||
<h2 class="mb-4 text-balance text-4xl font-extrabold tracking-tight text-neutral-800 dark:text-neutral-200">
|
<h2 class="mb-4 text-balance text-4xl font-extrabold tracking-tight text-neutral-800 dark:text-neutral-200">
|
||||||
{title}
|
{title}
|
||||||
</h2>
|
</h2>
|
||||||
<p class="mb-4 max-w-prose text-pretty font-light text-neutral-600 dark:text-neutral-400 sm:text-lg">
|
<p class="mb-4 max-w-prose text-pretty font-light text-neutral-600 dark:text-neutral-400 sm:text-lg">
|
||||||
{subTitle}
|
{subTitle}
|
||||||
</p>
|
</p>
|
||||||
{
|
{
|
||||||
btnExists ? <PrimaryCTA title={btnTitle} url={btnURL} /> : null
|
btnExists ? <PrimaryCTA title={btnTitle} url={btnURL} /> : null
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,79 +1,79 @@
|
|||||||
---
|
---
|
||||||
import PrimaryCTA from '@components/buttons/PrimaryCTA.astro'
|
import PrimaryCTA from '@components/buttons/PrimaryCTA.astro'
|
||||||
import { Image } from 'astro:assets'
|
import { Image } from 'astro:assets'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
subTitle,
|
subTitle,
|
||||||
btnExists,
|
btnExists,
|
||||||
btnTitle,
|
btnTitle,
|
||||||
btnURL,
|
btnURL,
|
||||||
single,
|
single,
|
||||||
imgOne,
|
imgOne,
|
||||||
imgOneAlt,
|
imgOneAlt,
|
||||||
imgTwo,
|
imgTwo,
|
||||||
imgTwoAlt,
|
imgTwoAlt,
|
||||||
} = Astro.props
|
} = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
subTitle: string
|
subTitle: string
|
||||||
btnExists?: boolean
|
btnExists?: boolean
|
||||||
btnTitle?: string
|
btnTitle?: string
|
||||||
btnURL?: string
|
btnURL?: string
|
||||||
single?: boolean
|
single?: boolean
|
||||||
imgOne?: any
|
imgOne?: any
|
||||||
imgOneAlt?: any
|
imgOneAlt?: any
|
||||||
imgTwo?: any
|
imgTwo?: any
|
||||||
imgTwoAlt?: any
|
imgTwoAlt?: any
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="mx-auto max-w-[85rem] items-center gap-16 px-4 py-10 sm:px-6 lg:grid lg:grid-cols-2 lg:px-8 lg:py-14 2xl:max-w-full">
|
<section class="mx-auto max-w-[85rem] items-center gap-16 px-4 py-10 sm:px-6 lg:grid lg:grid-cols-2 lg:px-8 lg:py-14 2xl:max-w-full">
|
||||||
<div>
|
<div>
|
||||||
<h2 class="mb-4 text-balance text-4xl font-extrabold tracking-tight text-neutral-800 dark:text-neutral-200">
|
<h2 class="mb-4 text-balance text-4xl font-extrabold tracking-tight text-neutral-800 dark:text-neutral-200">
|
||||||
{title}
|
{title}
|
||||||
</h2>
|
</h2>
|
||||||
<p class="mb-4 max-w-prose text-pretty font-light text-neutral-600 dark:text-neutral-400 sm:text-lg">
|
<p class="mb-4 max-w-prose text-pretty font-light text-neutral-600 dark:text-neutral-400 sm:text-lg">
|
||||||
{subTitle}
|
{subTitle}
|
||||||
</p>
|
</p>
|
||||||
{
|
{
|
||||||
btnExists ? <PrimaryCTA title={btnTitle} url={btnURL} /> : null
|
btnExists ? <PrimaryCTA title={btnTitle} url={btnURL} /> : null
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
single
|
single
|
||||||
? (
|
? (
|
||||||
<div class="mt-8">
|
<div class="mt-8">
|
||||||
<Image
|
<Image
|
||||||
class="w-full rounded-lg"
|
class="w-full rounded-lg"
|
||||||
src={imgOne}
|
src={imgOne}
|
||||||
alt={imgOneAlt}
|
alt={imgOneAlt}
|
||||||
format="avif"
|
format="avif"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
<div class="mt-8 grid grid-cols-2 gap-4">
|
<div class="mt-8 grid grid-cols-2 gap-4">
|
||||||
<Image
|
<Image
|
||||||
class="w-full rounded-xl"
|
class="w-full rounded-xl"
|
||||||
src={imgOne}
|
src={imgOne}
|
||||||
alt={imgOneAlt}
|
alt={imgOneAlt}
|
||||||
draggable="false"
|
draggable="false"
|
||||||
format="avif"
|
format="avif"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
<Image
|
<Image
|
||||||
class="mt-4 w-full rounded-xl lg:mt-10"
|
class="mt-4 w-full rounded-xl lg:mt-10"
|
||||||
src={imgTwo}
|
src={imgTwo}
|
||||||
alt={imgTwoAlt}
|
alt={imgTwoAlt}
|
||||||
draggable="false"
|
draggable="false"
|
||||||
format="avif"
|
format="avif"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,98 +1,98 @@
|
|||||||
---
|
---
|
||||||
import Icon from '@components/icons/icon.astro';
|
import Icon from '@components/icons/icon.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="focus-visible:ring-secondary group inline-flex items-center rounded-lg p-2.5 text-neutral-600 outline-none ring-zinc-500 transition duration-300 hover:bg-neutral-100 focus:outline-none focus-visible:outline-none focus-visible:ring-1 dark:text-neutral-400 dark:ring-zinc-200 dark:hover:bg-neutral-700"
|
class="focus-visible:ring-secondary group inline-flex items-center rounded-lg p-2.5 text-neutral-600 outline-none ring-zinc-500 transition duration-300 hover:bg-neutral-100 focus:outline-none focus-visible:outline-none focus-visible:ring-1 dark:text-neutral-400 dark:ring-zinc-200 dark:hover:bg-neutral-700"
|
||||||
data-bookmark-button="bookmark-button"
|
data-bookmark-button="bookmark-button"
|
||||||
>
|
>
|
||||||
<Icon name="bookmark" />
|
<Icon name="bookmark" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
class Bookmark {
|
class Bookmark {
|
||||||
private static readonly BOOKMARKS_KEY = "bookmarks";
|
private static readonly BOOKMARKS_KEY = "bookmarks";
|
||||||
private bookmarkButton: Element | null;
|
private bookmarkButton: Element | null;
|
||||||
|
|
||||||
constructor(private dataAttrValue: string) {
|
constructor(private dataAttrValue: string) {
|
||||||
this.bookmarkButton = document.querySelector(
|
this.bookmarkButton = document.querySelector(
|
||||||
`[data-bookmark-button="${dataAttrValue}"]`
|
`[data-bookmark-button="${dataAttrValue}"]`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getStoredBookmarks(): string[] {
|
private getStoredBookmarks(): string[] {
|
||||||
const item = localStorage.getItem(Bookmark.BOOKMARKS_KEY);
|
const item = localStorage.getItem(Bookmark.BOOKMARKS_KEY);
|
||||||
return item ? JSON.parse(item) : [];
|
return item ? JSON.parse(item) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
init(): void {
|
init(): void {
|
||||||
if (this.bookmarkButton && this.isStored()) {
|
if (this.bookmarkButton && this.isStored()) {
|
||||||
this.markAsStored();
|
this.markAsStored();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.bookmarkButton?.addEventListener("click", () =>
|
this.bookmarkButton?.addEventListener("click", () =>
|
||||||
this.toggleBookmark()
|
this.toggleBookmark()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
isStored(): boolean {
|
isStored(): boolean {
|
||||||
return this.getStoredBookmarks().includes(window.location.pathname);
|
return this.getStoredBookmarks().includes(window.location.pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
markAsStored(): void {
|
markAsStored(): void {
|
||||||
if (this.bookmarkButton) {
|
if (this.bookmarkButton) {
|
||||||
this.bookmarkButton.classList.add("bookmarked");
|
this.bookmarkButton.classList.add("bookmarked");
|
||||||
let svgElement = this.bookmarkButton.querySelector("svg");
|
let svgElement = this.bookmarkButton.querySelector("svg");
|
||||||
if (svgElement) {
|
if (svgElement) {
|
||||||
svgElement.setAttribute(
|
svgElement.setAttribute(
|
||||||
"class",
|
"class",
|
||||||
"h-6 w-6 fill-red-500 dark:fill-red-500"
|
"h-6 w-6 fill-red-500 dark:fill-red-500"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let pathElement = svgElement?.querySelector("path");
|
let pathElement = svgElement?.querySelector("path");
|
||||||
if (pathElement) {
|
if (pathElement) {
|
||||||
pathElement.setAttribute(
|
pathElement.setAttribute(
|
||||||
"class",
|
"class",
|
||||||
"fill-current text-red-500 dark:text-red-500"
|
"fill-current text-red-500 dark:text-red-500"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unmarkAsStored(): void {
|
unmarkAsStored(): void {
|
||||||
if (this.bookmarkButton) {
|
if (this.bookmarkButton) {
|
||||||
this.bookmarkButton.classList.remove("bookmarked");
|
this.bookmarkButton.classList.remove("bookmarked");
|
||||||
let svgElement = this.bookmarkButton.querySelector("svg");
|
let svgElement = this.bookmarkButton.querySelector("svg");
|
||||||
if (svgElement) {
|
if (svgElement) {
|
||||||
svgElement.setAttribute("class", "h-6 w-6 fill-none");
|
svgElement.setAttribute("class", "h-6 w-6 fill-none");
|
||||||
}
|
}
|
||||||
let pathElement = svgElement?.querySelector("path");
|
let pathElement = svgElement?.querySelector("path");
|
||||||
if (pathElement) {
|
if (pathElement) {
|
||||||
pathElement.setAttribute(
|
pathElement.setAttribute(
|
||||||
"class",
|
"class",
|
||||||
"fill-current text-neutral-500 group-hover:text-red-400 dark:text-neutral-500 group-hover:dark:text-red-400"
|
"fill-current text-neutral-500 group-hover:text-red-400 dark:text-neutral-500 group-hover:dark:text-red-400"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleBookmark(): void {
|
toggleBookmark(): void {
|
||||||
let storedBookmarks = this.getStoredBookmarks();
|
let storedBookmarks = this.getStoredBookmarks();
|
||||||
const index = storedBookmarks.indexOf(window.location.pathname);
|
const index = storedBookmarks.indexOf(window.location.pathname);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
storedBookmarks.splice(index, 1);
|
storedBookmarks.splice(index, 1);
|
||||||
this.unmarkAsStored();
|
this.unmarkAsStored();
|
||||||
} else {
|
} else {
|
||||||
storedBookmarks.push(window.location.pathname);
|
storedBookmarks.push(window.location.pathname);
|
||||||
this.markAsStored();
|
this.markAsStored();
|
||||||
}
|
}
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
Bookmark.BOOKMARKS_KEY,
|
Bookmark.BOOKMARKS_KEY,
|
||||||
JSON.stringify(storedBookmarks)
|
JSON.stringify(storedBookmarks)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new Bookmark("bookmark-button").init();
|
new Bookmark("bookmark-button").init();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
---
|
---
|
||||||
import Icon from '@components/icons/icon.astro'
|
import Icon from '@components/icons/icon.astro'
|
||||||
|
|
||||||
const { title, url } = Astro.props
|
const { title, url } = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title?: string
|
title?: string
|
||||||
url?: string
|
url?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseClasses = 'group inline-flex items-center justify-center gap-x-3 rounded-full px-4 py-3 text-center text-sm font-medium text-neutral-700 ring-zinc-500 focus-visible:ring transition duration-300 outline-none'
|
const baseClasses = 'group inline-flex items-center justify-center gap-x-3 rounded-full px-4 py-3 text-center text-sm font-medium text-neutral-700 ring-zinc-500 focus-visible:ring transition duration-300 outline-none'
|
||||||
const borderClasses = 'border border-transparent'
|
const borderClasses = 'border border-transparent'
|
||||||
const bgColorClasses = 'bg-yellow-400 dark:focus:outline-none'
|
const bgColorClasses = 'bg-yellow-400 dark:focus:outline-none'
|
||||||
const hoverClasses = 'hover:shadow-2xl hover:shadow-yellow-500'
|
const hoverClasses = 'hover:shadow-2xl hover:shadow-yellow-500'
|
||||||
const fontSizeClasses = '2xl:text-base'
|
const fontSizeClasses = '2xl:text-base'
|
||||||
const ringClasses = 'dark:ring-zinc-200';
|
const ringClasses = 'dark:ring-zinc-200';
|
||||||
---
|
---
|
||||||
|
|
||||||
<a
|
<a
|
||||||
class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${hoverClasses} ${fontSizeClasses} ${ringClasses}`}
|
class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${hoverClasses} ${fontSizeClasses} ${ringClasses}`}
|
||||||
href={url}
|
href={url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
<Icon name="github" />
|
<Icon name="github" />
|
||||||
{title}
|
{title}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
---
|
---
|
||||||
import Icon from '@components/icons/icon.astro'
|
import Icon from '@components/icons/icon.astro'
|
||||||
|
|
||||||
const { title, id, noArrow } = Astro.props
|
const { title, id, noArrow } = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title?: string
|
title?: string
|
||||||
id?: string
|
id?: string
|
||||||
noArrow?: boolean
|
noArrow?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseClasses = 'group inline-flex items-center justify-center gap-x-2 rounded-lg px-4 py-3 text-sm font-bold text-neutral-50 ring-zinc-500 transition duration-300 focus-visible:ring outline-none'
|
const baseClasses = 'group inline-flex items-center justify-center gap-x-2 rounded-lg px-4 py-3 text-sm font-bold text-neutral-50 ring-zinc-500 transition duration-300 focus-visible:ring outline-none'
|
||||||
const borderClasses = 'border border-transparent'
|
const borderClasses = 'border border-transparent'
|
||||||
const bgColorClasses = 'bg-orange-400 hover:bg-orange-500 active:bg-orange-500 dark:focus:outline-none'
|
const bgColorClasses = 'bg-orange-400 hover:bg-orange-500 active:bg-orange-500 dark:focus:outline-none'
|
||||||
const disableClasses = 'disabled:pointer-events-none disabled:opacity-50'
|
const disableClasses = 'disabled:pointer-events-none disabled:opacity-50'
|
||||||
const fontSizeClasses = '2xl:text-base'
|
const fontSizeClasses = '2xl:text-base'
|
||||||
const ringClasses = 'dark:ring-zinc-200';
|
const ringClasses = 'dark:ring-zinc-200';
|
||||||
---
|
---
|
||||||
|
|
||||||
<button class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${disableClasses} ${fontSizeClasses} ${ringClasses}`} id={id}>
|
<button class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${disableClasses} ${fontSizeClasses} ${ringClasses}`} id={id}>
|
||||||
{title}
|
{title}
|
||||||
{noArrow ? null : <Icon name="arrowRight" />}
|
{noArrow ? null : <Icon name="arrowRight" />}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
---
|
---
|
||||||
import Icon from '@components/icons/icon.astro'
|
import Icon from '@components/icons/icon.astro'
|
||||||
|
|
||||||
const { title, url, noArrow } = Astro.props
|
const { title, url, noArrow } = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title?: string
|
title?: string
|
||||||
url?: string
|
url?: string
|
||||||
noArrow?: boolean
|
noArrow?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseClasses = 'group inline-flex items-center justify-center gap-x-2 rounded-lg px-4 py-3 text-sm font-bold text-neutral-50 ring-zinc-500 transition duration-300 focus-visible:ring outline-none'
|
const baseClasses = 'group inline-flex items-center justify-center gap-x-2 rounded-lg px-4 py-3 text-sm font-bold text-neutral-50 ring-zinc-500 transition duration-300 focus-visible:ring outline-none'
|
||||||
const borderClasses = 'border border-transparent'
|
const borderClasses = 'border border-transparent'
|
||||||
const bgColorClasses = 'bg-orange-400 hover:bg-orange-500 active:bg-orange-500 dark:focus:outline-none'
|
const bgColorClasses = 'bg-orange-400 hover:bg-orange-500 active:bg-orange-500 dark:focus:outline-none'
|
||||||
const disableClasses = 'disabled:pointer-events-none disabled:opacity-50'
|
const disableClasses = 'disabled:pointer-events-none disabled:opacity-50'
|
||||||
const fontSizeClasses = '2xl:text-base'
|
const fontSizeClasses = '2xl:text-base'
|
||||||
const ringClasses = 'dark:ring-zinc-200';
|
const ringClasses = 'dark:ring-zinc-200';
|
||||||
---
|
---
|
||||||
|
|
||||||
<a
|
<a
|
||||||
class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${disableClasses} ${fontSizeClasses} ${ringClasses}`}
|
class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${disableClasses} ${fontSizeClasses} ${ringClasses}`}
|
||||||
href={url}
|
href={url}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
{noArrow ? null : <Icon name="arrowRight" />}
|
{noArrow ? null : <Icon name="arrowRight" />}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
---
|
---
|
||||||
const { title, url } = Astro.props
|
const { title, url } = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title?: string
|
title?: string
|
||||||
url?: string
|
url?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseClasses = 'inline-flex items-center justify-center gap-x-2 rounded-lg px-4 py-3 text-center text-sm font-medium text-neutral-600 shadow-sm outline-none ring-zinc-500 focus-visible:ring transition duration-300'
|
const baseClasses = 'inline-flex items-center justify-center gap-x-2 rounded-lg px-4 py-3 text-center text-sm font-medium text-neutral-600 shadow-sm outline-none ring-zinc-500 focus-visible:ring transition duration-300'
|
||||||
const borderClasses = 'border border-neutral-200'
|
const borderClasses = 'border border-neutral-200'
|
||||||
const bgColorClasses = 'bg-neutral-300'
|
const bgColorClasses = 'bg-neutral-300'
|
||||||
const hoverClasses = 'hover:bg-neutral-400/50 hover:text-neutral-600 active:text-neutral-700'
|
const hoverClasses = 'hover:bg-neutral-400/50 hover:text-neutral-600 active:text-neutral-700'
|
||||||
const disableClasses = 'disabled:pointer-events-none disabled:opacity-50'
|
const disableClasses = 'disabled:pointer-events-none disabled:opacity-50'
|
||||||
const fontSizeClasses = '2xl:text-base'
|
const fontSizeClasses = '2xl:text-base'
|
||||||
const ringClasses = 'ring-zinc-500'
|
const ringClasses = 'ring-zinc-500'
|
||||||
const darkClasses = 'dark:border-neutral-700 dark:bg-zinc-700 dark:text-neutral-300 dark:ring-zinc-200 dark:hover:bg-zinc-600 dark:focus:outline-none';
|
const darkClasses = 'dark:border-neutral-700 dark:bg-zinc-700 dark:text-neutral-300 dark:ring-zinc-200 dark:hover:bg-zinc-600 dark:focus:outline-none';
|
||||||
---
|
---
|
||||||
|
|
||||||
<a
|
<a
|
||||||
class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${hoverClasses} ${disableClasses} ${fontSizeClasses} ${ringClasses} ${darkClasses}`}
|
class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${hoverClasses} ${disableClasses} ${fontSizeClasses} ${ringClasses} ${darkClasses}`}
|
||||||
href={url}
|
href={url}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,151 +1,151 @@
|
|||||||
---
|
---
|
||||||
import Icon from '@components/icons/icon.astro'
|
import Icon from '@components/icons/icon.astro'
|
||||||
|
|
||||||
const { pageTitle, title = 'Share' } = Astro.props
|
const { pageTitle, title = 'Share' } = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
pageTitle: string
|
pageTitle: string
|
||||||
title?: string
|
title?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SocialPlatform = {
|
type SocialPlatform = {
|
||||||
name: string
|
name: string
|
||||||
url: string
|
url: string
|
||||||
svg: string
|
svg: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const socialPlatforms: SocialPlatform[] = [
|
const socialPlatforms: SocialPlatform[] = [
|
||||||
{
|
{
|
||||||
name: 'Facebook',
|
name: 'Facebook',
|
||||||
url: `https://www.facebook.com/share.php?u=${Astro.url}&title=${pageTitle}`,
|
url: `https://www.facebook.com/share.php?u=${Astro.url}&title=${pageTitle}`,
|
||||||
svg: 'facebook',
|
svg: 'facebook',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'X',
|
name: 'X',
|
||||||
url: `https://twitter.com/home/?status=${pageTitle}${Astro.url}`,
|
url: `https://twitter.com/home/?status=${pageTitle}${Astro.url}`,
|
||||||
svg: 'x',
|
svg: 'x',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'LinkedIn',
|
name: 'LinkedIn',
|
||||||
url: `https://www.linkedin.com/shareArticle?mini=true&url=${Astro.url}&title=${pageTitle}`,
|
url: `https://www.linkedin.com/shareArticle?mini=true&url=${Astro.url}&title=${pageTitle}`,
|
||||||
svg: 'linkedIn',
|
svg: 'linkedIn',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="hs-dropdown relative inline-flex [--auto-close:inside] [--placement:top-left]">
|
<div class="hs-dropdown relative inline-flex [--auto-close:inside] [--placement:top-left]">
|
||||||
<button
|
<button
|
||||||
id="hs-dropup"
|
id="hs-dropup"
|
||||||
type="button"
|
type="button"
|
||||||
class="hs-dropdown-toggle inline-flex items-center gap-x-2 rounded-lg px-4 py-3 text-sm font-medium text-neutral-600 outline-none ring-zinc-500 transition duration-300 hover:bg-neutral-100 hover:text-neutral-700 focus-visible:ring dark:text-neutral-400 dark:ring-zinc-200 dark:hover:bg-neutral-700 dark:hover:text-neutral-300 dark:focus:outline-none"
|
class="hs-dropdown-toggle inline-flex items-center gap-x-2 rounded-lg px-4 py-3 text-sm font-medium text-neutral-600 outline-none ring-zinc-500 transition duration-300 hover:bg-neutral-100 hover:text-neutral-700 focus-visible:ring dark:text-neutral-400 dark:ring-zinc-200 dark:hover:bg-neutral-700 dark:hover:text-neutral-300 dark:focus:outline-none"
|
||||||
>
|
>
|
||||||
<Icon name="share" />
|
<Icon name="share" />
|
||||||
|
|
||||||
{title}
|
{title}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="hs-dropdown-menu duration z-10 hidden w-72 divide-y divide-neutral-200 rounded-lg bg-neutral-50 p-2 opacity-0 shadow-md transition-[opacity,margin] hs-dropdown-open:opacity-100 dark:divide-neutral-700 dark:border dark:border-neutral-700 dark:bg-neutral-800"
|
class="hs-dropdown-menu duration z-10 hidden w-72 divide-y divide-neutral-200 rounded-lg bg-neutral-50 p-2 opacity-0 shadow-md transition-[opacity,margin] hs-dropdown-open:opacity-100 dark:divide-neutral-700 dark:border dark:border-neutral-700 dark:bg-neutral-800"
|
||||||
aria-labelledby="hs-dropup"
|
aria-labelledby="hs-dropup"
|
||||||
>
|
>
|
||||||
<div class="py-2 first:pt-0 last:pb-0">
|
<div class="py-2 first:pt-0 last:pb-0">
|
||||||
{
|
{
|
||||||
socialPlatforms.map(platform => (
|
socialPlatforms.map(platform => (
|
||||||
<a
|
<a
|
||||||
class="flex items-center gap-x-3.5 rounded-lg px-3 py-2 text-sm text-neutral-700 hover:bg-neutral-200 focus:bg-neutral-100 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-700 dark:hover:text-neutral-300 dark:focus:bg-neutral-700 "
|
class="flex items-center gap-x-3.5 rounded-lg px-3 py-2 text-sm text-neutral-700 hover:bg-neutral-200 focus:bg-neutral-100 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-700 dark:hover:text-neutral-300 dark:focus:bg-neutral-700 "
|
||||||
href={platform.url}
|
href={platform.url}
|
||||||
>
|
>
|
||||||
<Icon name={platform.svg} />
|
<Icon name={platform.svg} />
|
||||||
Share on {platform.name}
|
Share on {platform.name}
|
||||||
</a>
|
</a>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="py-2 first:pt-0 last:pb-0">
|
<div class="py-2 first:pt-0 last:pb-0">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="js-clipboard hover:text-dark focus-visible:ring-secondary group inline-flex w-full items-center gap-x-3.5 rounded-lg px-3 py-2 text-sm text-neutral-700 hover:bg-neutral-200 focus:bg-neutral-100 focus:outline-none focus-visible:outline-none focus-visible:ring-1 dark:text-neutral-300 dark:hover:bg-neutral-700 dark:hover:text-neutral-300 dark:focus:bg-neutral-700"
|
class="js-clipboard hover:text-dark focus-visible:ring-secondary group inline-flex w-full items-center gap-x-3.5 rounded-lg px-3 py-2 text-sm text-neutral-700 hover:bg-neutral-200 focus:bg-neutral-100 focus:outline-none focus-visible:outline-none focus-visible:ring-1 dark:text-neutral-300 dark:hover:bg-neutral-700 dark:hover:text-neutral-300 dark:focus:bg-neutral-700"
|
||||||
data-clipboard-success-text="Copied"
|
data-clipboard-success-text="Copied"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="js-clipboard-default h-4 w-4 transition group-hover:rotate-6"
|
class="js-clipboard-default h-4 w-4 transition group-hover:rotate-6"
|
||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<rect width="8" height="4" x="8" y="2" rx="1" ry="1"></rect>
|
<rect width="8" height="4" x="8" y="2" rx="1" ry="1"></rect>
|
||||||
<path
|
<path
|
||||||
d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"
|
d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"
|
||||||
></path>
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
class="js-clipboard-success text-neutral-700 dark:text-neutral-300 hidden h-4 w-4"
|
class="js-clipboard-success text-neutral-700 dark:text-neutral-300 hidden h-4 w-4"
|
||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<polyline points="20 6 9 17 4 12"></polyline>
|
<polyline points="20 6 9 17 4 12"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="js-clipboard-success-text">Copy link</span>
|
<span class="js-clipboard-success-text">Copy link</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--Import the necessary Dropdown and Clipboard plugins-->
|
<!--Import the necessary Dropdown and Clipboard plugins-->
|
||||||
<!--https://preline.co/plugins/html/dropdown.html-->
|
<!--https://preline.co/plugins/html/dropdown.html-->
|
||||||
<!--<script is:inline src="/scripts/vendor/preline/dropdown/index.js"></script>-->
|
<!--<script is:inline src="/scripts/vendor/preline/dropdown/index.js"></script>-->
|
||||||
|
|
||||||
<!-- https://clipboardjs.com/ -->
|
<!-- https://clipboardjs.com/ -->
|
||||||
<!--<script is:inline src="/scripts/vendor/clipboard.min.js"></script>-->
|
<!--<script is:inline src="/scripts/vendor/clipboard.min.js"></script>-->
|
||||||
|
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
(function () {
|
(function () {
|
||||||
window.addEventListener("load", () => {
|
window.addEventListener("load", () => {
|
||||||
const $clipboards = document.querySelectorAll(".js-clipboard");
|
const $clipboards = document.querySelectorAll(".js-clipboard");
|
||||||
$clipboards.forEach((el) => {
|
$clipboards.forEach((el) => {
|
||||||
const clipboard = new ClipboardJS(el, {
|
const clipboard = new ClipboardJS(el, {
|
||||||
text: () => {
|
text: () => {
|
||||||
return window.location.href;
|
return window.location.href;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
clipboard.on("success", () => {
|
clipboard.on("success", () => {
|
||||||
const $default = el.querySelector(".js-clipboard-default");
|
const $default = el.querySelector(".js-clipboard-default");
|
||||||
const $success = el.querySelector(".js-clipboard-success");
|
const $success = el.querySelector(".js-clipboard-success");
|
||||||
const $successText = el.querySelector(".js-clipboard-success-text");
|
const $successText = el.querySelector(".js-clipboard-success-text");
|
||||||
const successText = el.dataset.clipboardSuccessText || "";
|
const successText = el.dataset.clipboardSuccessText || "";
|
||||||
let oldSuccessText;
|
let oldSuccessText;
|
||||||
|
|
||||||
if ($successText) {
|
if ($successText) {
|
||||||
oldSuccessText = $successText.textContent;
|
oldSuccessText = $successText.textContent;
|
||||||
$successText.textContent = successText;
|
$successText.textContent = successText;
|
||||||
}
|
}
|
||||||
if ($default && $success) {
|
if ($default && $success) {
|
||||||
$default.style.display = "none";
|
$default.style.display = "none";
|
||||||
$success.style.display = "block";
|
$success.style.display = "block";
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
if ($successText && oldSuccessText)
|
if ($successText && oldSuccessText)
|
||||||
$successText.textContent = oldSuccessText;
|
$successText.textContent = oldSuccessText;
|
||||||
if ($default && $success) {
|
if ($default && $success) {
|
||||||
$success.style.display = "";
|
$success.style.display = "";
|
||||||
$default.style.display = "";
|
$default.style.display = "";
|
||||||
}
|
}
|
||||||
}, 800);
|
}, 800);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,40 +1,40 @@
|
|||||||
---
|
---
|
||||||
import { Icons } from './icons.ts'
|
import { Icons } from './icons.ts'
|
||||||
|
|
||||||
interface Path {
|
interface Path {
|
||||||
d: string
|
d: string
|
||||||
class?: string
|
class?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const { name } = Astro.props
|
const { name } = Astro.props
|
||||||
|
|
||||||
const icon = (Icons as any)[name] || {}
|
const icon = (Icons as any)[name] || {}
|
||||||
|
|
||||||
const paths: Path[] = icon.paths || [];
|
const paths: Path[] = icon.paths || [];
|
||||||
---
|
---
|
||||||
|
|
||||||
{
|
{
|
||||||
icon
|
icon
|
||||||
? (
|
? (
|
||||||
<svg
|
<svg
|
||||||
class={icon.class}
|
class={icon.class}
|
||||||
height={icon.height}
|
height={icon.height}
|
||||||
viewBox={icon.viewBox}
|
viewBox={icon.viewBox}
|
||||||
width={icon.width}
|
width={icon.width}
|
||||||
fill={icon.fill}
|
fill={icon.fill}
|
||||||
clip-rule={icon.clipRule}
|
clip-rule={icon.clipRule}
|
||||||
fill-rule={icon.fillRule}
|
fill-rule={icon.fillRule}
|
||||||
stroke={icon.stroke}
|
stroke={icon.stroke}
|
||||||
stroke-width={icon.strokeWidth}
|
stroke-width={icon.strokeWidth}
|
||||||
stroke-linecap={icon.strokeLinecap}
|
stroke-linecap={icon.strokeLinecap}
|
||||||
stroke-linejoin={icon.strokeLinejoin}
|
stroke-linejoin={icon.strokeLinejoin}
|
||||||
><title>{icon.title}</title>
|
><title>{icon.title}</title>
|
||||||
{paths.map(path => (
|
{paths.map(path => (
|
||||||
<path d={path.d} class={path.class || ''} />
|
<path d={path.d} class={path.class || ''} />
|
||||||
))}
|
))}
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
'Icon not found'
|
'Icon not found'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,414 +1,414 @@
|
|||||||
export const Icons = {
|
export const Icons = {
|
||||||
groups: {
|
groups: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'm150-400 82-80-82-82-80 82 80 80Zm573-10 87-140 88 140H723Zm-243-70q-50 0-85-35t-35-85q0-51 35-85.5t85-34.5q51 0 85.5 34.5T600-600q0 50-34.5 85T480-480Zm.351-180Q455-660 437.5-642.851t-17.5 42.5Q420-575 437.351-557.5t43 17.5Q506-540 523-557.351t17-43Q540-626 522.851-643t-42.5-17ZM480-600ZM0-240v-53q0-39.464 42-63.232T150.398-380q12.158 0 23.38.5T196-377.273q-8 17.273-12 34.842-4 17.57-4 37.431v65H0Zm240 0v-65q0-65 66.5-105T480-450q108 0 174 40t66 105v65H240Zm570-140q67.5 0 108.75 23.768T960-293v53H780v-65q0-19.861-3.5-37.431Q773-360 765-377.273q11-1.727 22.171-2.227 11.172-.5 22.829-.5Zm-330.2-10Q400-390 350-366q-50 24-50 61v5h360v-6q0-36-49.5-60t-130.7-24Zm.2 90Z',
|
d: 'm150-400 82-80-82-82-80 82 80 80Zm573-10 87-140 88 140H723Zm-243-70q-50 0-85-35t-35-85q0-51 35-85.5t85-34.5q51 0 85.5 34.5T600-600q0 50-34.5 85T480-480Zm.351-180Q455-660 437.5-642.851t-17.5 42.5Q420-575 437.351-557.5t43 17.5Q506-540 523-557.351t17-43Q540-626 522.851-643t-42.5-17ZM480-600ZM0-240v-53q0-39.464 42-63.232T150.398-380q12.158 0 23.38.5T196-377.273q-8 17.273-12 34.842-4 17.57-4 37.431v65H0Zm240 0v-65q0-65 66.5-105T480-450q108 0 174 40t66 105v65H240Zm570-140q67.5 0 108.75 23.768T960-293v53H780v-65q0-19.861-3.5-37.431Q773-360 765-377.273q11-1.727 22.171-2.227 11.172-.5 22.829-.5Zm-330.2-10Q400-390 350-366q-50 24-50 61v5h360v-6q0-36-49.5-60t-130.7-24Zm.2 90Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'mt-1 h-8 w-8 flex-shrink-0 fill-orange-400 dark:fill-orange-300',
|
class: 'mt-1 h-8 w-8 flex-shrink-0 fill-orange-400 dark:fill-orange-300',
|
||||||
width: 48,
|
width: 48,
|
||||||
height: 48,
|
height: 48,
|
||||||
viewBox: '0 -960 960 960',
|
viewBox: '0 -960 960 960',
|
||||||
},
|
},
|
||||||
books: {
|
books: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M343-420h225v-60H343v60Zm0-90h395v-60H343v60Zm0-90h395v-60H343v60Zm-83 400q-24 0-42-18t-18-42v-560q0-24 18-42t42-18h560q24 0 42 18t18 42v560q0 24-18 42t-42 18H260Zm0-60h560v-560H260v560ZM140-80q-24 0-42-18t-18-42v-620h60v620h620v60H140Zm120-740v560-560Z',
|
d: 'M343-420h225v-60H343v60Zm0-90h395v-60H343v60Zm0-90h395v-60H343v60Zm-83 400q-24 0-42-18t-18-42v-560q0-24 18-42t42-18h560q24 0 42 18t18 42v560q0 24-18 42t-42 18H260Zm0-60h560v-560H260v560ZM140-80q-24 0-42-18t-18-42v-620h60v620h620v60H140Zm120-740v560-560Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'mt-1 h-8 w-8 flex-shrink-0 fill-orange-400 dark:fill-orange-300',
|
class: 'mt-1 h-8 w-8 flex-shrink-0 fill-orange-400 dark:fill-orange-300',
|
||||||
width: 48,
|
width: 48,
|
||||||
height: 48,
|
height: 48,
|
||||||
viewBox: '0 -960 960 960',
|
viewBox: '0 -960 960 960',
|
||||||
},
|
},
|
||||||
verified: {
|
verified: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'm346-60-76-130-151-31 17-147-96-112 96-111-17-147 151-31 76-131 134 62 134-62 77 131 150 31-17 147 96 111-96 112 17 147-150 31-77 130-134-62-134 62Zm27-79 107-45 110 45 67-100 117-30-12-119 81-92-81-94 12-119-117-28-69-100-108 45-110-45-67 100-117 28 12 119-81 94 81 92-12 121 117 28 70 100Zm107-341Zm-43 133 227-225-45-41-182 180-95-99-46 45 141 140Z',
|
d: 'm346-60-76-130-151-31 17-147-96-112 96-111-17-147 151-31 76-131 134 62 134-62 77 131 150 31-17 147 96 111-96 112 17 147-150 31-77 130-134-62-134 62Zm27-79 107-45 110 45 67-100 117-30-12-119 81-92-81-94 12-119-117-28-69-100-108 45-110-45-67 100-117 28 12 119-81 94 81 92-12 121 117 28 70 100Zm107-341Zm-43 133 227-225-45-41-182 180-95-99-46 45 141 140Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'mt-1 h-8 w-8 flex-shrink-0 fill-orange-400 dark:fill-orange-300',
|
class: 'mt-1 h-8 w-8 flex-shrink-0 fill-orange-400 dark:fill-orange-300',
|
||||||
width: 48,
|
width: 48,
|
||||||
height: 48,
|
height: 48,
|
||||||
viewBox: '0 -960 960 960',
|
viewBox: '0 -960 960 960',
|
||||||
},
|
},
|
||||||
frame: {
|
frame: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M480-480q-51 0-85.5-34.5T360-600q0-50 34.5-85t85.5-35q50 0 85 35t35 85q0 51-35 85.5T480-480Zm-.351-60Q505-540 522.5-557.149t17.5-42.5Q540-625 522.649-642.5t-43-17.5Q454-660 437-642.649t-17 43Q420-574 437.149-557t42.5 17ZM240-240v-76q0-27 17.5-47.5T300-397q42-22 86.943-32.5 44.942-10.5 93-10.5Q528-440 573-429.5t87 32.5q25 13 42.5 33.5T720-316v76H240Zm240-140q-47.546 0-92.773 13T300-328v28h360v-28q-42-26-87.227-39-45.227-13-92.773-13Zm0-220Zm0 300h180-360 180ZM140-80q-24 0-42-18t-18-42v-172h60v172h172v60H140ZM80-648v-172q0-24 18-42t42-18h172v60H140v172H80ZM648-80v-60h172v-172h60v172q0 24-18 42t-42 18H648Zm172-568v-172H648v-60h172q24 0 42 18t18 42v172h-60Z',
|
d: 'M480-480q-51 0-85.5-34.5T360-600q0-50 34.5-85t85.5-35q50 0 85 35t35 85q0 51-35 85.5T480-480Zm-.351-60Q505-540 522.5-557.149t17.5-42.5Q540-625 522.649-642.5t-43-17.5Q454-660 437-642.649t-17 43Q420-574 437.149-557t42.5 17ZM240-240v-76q0-27 17.5-47.5T300-397q42-22 86.943-32.5 44.942-10.5 93-10.5Q528-440 573-429.5t87 32.5q25 13 42.5 33.5T720-316v76H240Zm240-140q-47.546 0-92.773 13T300-328v28h360v-28q-42-26-87.227-39-45.227-13-92.773-13Zm0-220Zm0 300h180-360 180ZM140-80q-24 0-42-18t-18-42v-172h60v172h172v60H140ZM80-648v-172q0-24 18-42t42-18h172v60H140v172H80ZM648-80v-60h172v-172h60v172q0 24-18 42t-42 18H648Zm172-568v-172H648v-60h172q24 0 42 18t18 42v172h-60Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'mt-1 h-8 w-8 flex-shrink-0 fill-orange-400 dark:fill-orange-300',
|
class: 'mt-1 h-8 w-8 flex-shrink-0 fill-orange-400 dark:fill-orange-300',
|
||||||
width: 48,
|
width: 48,
|
||||||
height: 48,
|
height: 48,
|
||||||
viewBox: '0 -960 960 960',
|
viewBox: '0 -960 960 960',
|
||||||
},
|
},
|
||||||
tools: {
|
tools: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M764-80q-6 0-11-2t-10-7L501-331q-5-5-7-10t-2-11q0-6 2-11t7-10l85-85q5-5 10-7t11-2q6 0 11 2t10 7l242 242q5 5 7 10t2 11q0 6-2 11t-7 10l-85 85q-5 5-10 7t-11 2Zm0-72 43-43-200-200-43 43 200 200ZM195-80q-6 0-11.5-2T173-89l-84-84q-5-5-7-10.5T80-195q0-6 2-11t7-10l225-225h85l38-38-175-175h-57L80-779l99-99 125 125v57l175 175 130-130-67-67 56-56H485l-18-18 128-128 18 18v113l56-56 169 169q15 15 23.5 34.5T870-600q0 20-6.5 38.5T845-528l-85-85-56 56-52-52-211 211v84L216-89q-5 5-10 7t-11 2Zm0-72 200-200v-43h-43L152-195l43 43Zm0 0-43-43 22 21 21 22Zm569 0 43-43-43 43Z',
|
d: 'M764-80q-6 0-11-2t-10-7L501-331q-5-5-7-10t-2-11q0-6 2-11t7-10l85-85q5-5 10-7t11-2q6 0 11 2t10 7l242 242q5 5 7 10t2 11q0 6-2 11t-7 10l-85 85q-5 5-10 7t-11 2Zm0-72 43-43-200-200-43 43 200 200ZM195-80q-6 0-11.5-2T173-89l-84-84q-5-5-7-10.5T80-195q0-6 2-11t7-10l225-225h85l38-38-175-175h-57L80-779l99-99 125 125v57l175 175 130-130-67-67 56-56H485l-18-18 128-128 18 18v113l56-56 169 169q15 15 23.5 34.5T870-600q0 20-6.5 38.5T845-528l-85-85-56 56-52-52-211 211v84L216-89q-5 5-10 7t-11 2Zm0-72 200-200v-43h-43L152-195l43 43Zm0 0-43-43 22 21 21 22Zm569 0 43-43-43 43Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class:
|
class:
|
||||||
'mt-2 h-6 w-6 flex-shrink-0 fill-neutral-700 hs-tab-active:fill-orange-400 dark:fill-neutral-300 dark:hs-tab-active:fill-orange-300 md:h-7 md:w-7',
|
'mt-2 h-6 w-6 flex-shrink-0 fill-neutral-700 hs-tab-active:fill-orange-400 dark:fill-neutral-300 dark:hs-tab-active:fill-orange-300 md:h-7 md:w-7',
|
||||||
width: 48,
|
width: 48,
|
||||||
height: 48,
|
height: 48,
|
||||||
viewBox: '0 -960 960 960',
|
viewBox: '0 -960 960 960',
|
||||||
},
|
},
|
||||||
dashboard: {
|
dashboard: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M510-570v-270h330v270H510ZM120-450v-390h330v390H120Zm390 330v-390h330v390H510Zm-390 0v-270h330v270H120Zm60-390h210v-270H180v270Zm390 330h210v-270H570v270Zm0-450h210v-150H570v150ZM180-180h210v-150H180v150Zm210-330Zm180-120Zm0 180ZM390-330Z',
|
d: 'M510-570v-270h330v270H510ZM120-450v-390h330v390H120Zm390 330v-390h330v390H510Zm-390 0v-270h330v270H120Zm60-390h210v-270H180v270Zm390 330h210v-270H570v270Zm0-450h210v-150H570v150ZM180-180h210v-150H180v150Zm210-330Zm180-120Zm0 180ZM390-330Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class:
|
class:
|
||||||
'mt-2 h-6 w-6 flex-shrink-0 fill-neutral-700 hs-tab-active:fill-orange-400 dark:fill-neutral-300 dark:hs-tab-active:fill-orange-300 md:h-7 md:w-7',
|
'mt-2 h-6 w-6 flex-shrink-0 fill-neutral-700 hs-tab-active:fill-orange-400 dark:fill-neutral-300 dark:hs-tab-active:fill-orange-300 md:h-7 md:w-7',
|
||||||
width: 48,
|
width: 48,
|
||||||
height: 48,
|
height: 48,
|
||||||
viewBox: '0 -960 960 960',
|
viewBox: '0 -960 960 960',
|
||||||
},
|
},
|
||||||
house: {
|
house: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M8.25 21v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21m0 0h4.5V3.545M12.75 21h7.5V10.75M2.25 21h1.5m18 0h-18M2.25 9l4.5-1.636M18.75 3l-1.5.545m0 6.205 3 1m1.5.5-1.5-.5M6.75 7.364V3h-3v18m3-13.636 10.5-3.819',
|
d: 'M8.25 21v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21m0 0h4.5V3.545M12.75 21h7.5V10.75M2.25 21h1.5m18 0h-18M2.25 9l4.5-1.636M18.75 3l-1.5.545m0 6.205 3 1m1.5.5-1.5-.5M6.75 7.364V3h-3v18m3-13.636 10.5-3.819',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class:
|
class:
|
||||||
'h-6 w-6 flex-shrink-0 text-neutral-700 hs-tab-active:text-orange-400 dark:text-neutral-300 dark:hs-tab-active:text-orange-300 md:h-7 md:w-7',
|
'h-6 w-6 flex-shrink-0 text-neutral-700 hs-tab-active:text-orange-400 dark:text-neutral-300 dark:hs-tab-active:text-orange-300 md:h-7 md:w-7',
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
strokeWidth: '1.5',
|
strokeWidth: '1.5',
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
strokeLinejoin: 'round',
|
strokeLinejoin: 'round',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
arrowUp: {
|
arrowUp: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'm5 12 7-7 7 7',
|
d: 'm5 12 7-7 7 7',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
d: 'M12 19V5',
|
d: 'M12 19V5',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'h-5 w-5 flex-shrink-0 text-orange-400 dark:text-orange-300',
|
class: 'h-5 w-5 flex-shrink-0 text-orange-400 dark:text-orange-300',
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
strokeWidth: '2',
|
strokeWidth: '2',
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
strokeLinejoin: 'round',
|
strokeLinejoin: 'round',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
checkCircle: {
|
checkCircle: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M10 18a8 8 0 100-16 8 8 0 000 16zM13.707 8.293a1 1 0 00-1.414-1.414L9 10.586l-1.293-1.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z',
|
d: 'M10 18a8 8 0 100-16 8 8 0 000 16zM13.707 8.293a1 1 0 00-1.414-1.414L9 10.586l-1.293-1.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'h-5 w-5 shrink-0',
|
class: 'h-5 w-5 shrink-0',
|
||||||
viewBox: '0 0 20 20',
|
viewBox: '0 0 20 20',
|
||||||
fill: 'currentColor',
|
fill: 'currentColor',
|
||||||
fillRule: 'evenodd',
|
fillRule: 'evenodd',
|
||||||
clipRule: 'evenodd',
|
clipRule: 'evenodd',
|
||||||
},
|
},
|
||||||
bookmark: {
|
bookmark: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z',
|
d: 'M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z',
|
||||||
class:
|
class:
|
||||||
'fill-current text-neutral-500 transition duration-300 group-hover:text-red-400 group-hover:dark:text-red-400',
|
'fill-current text-neutral-500 transition duration-300 group-hover:text-red-400 group-hover:dark:text-red-400',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'h-6 w-6 fill-none transition duration-300',
|
class: 'h-6 w-6 fill-none transition duration-300',
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
strokeWidth: '1.5',
|
strokeWidth: '1.5',
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
strokeLinejoin: 'round',
|
strokeLinejoin: 'round',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
arrowRight: {
|
arrowRight: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'm9 18 6-6-6-6',
|
d: 'm9 18 6-6-6-6',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'h-4 w-4 flex-shrink-0 transition duration-300 group-hover:translate-x-1',
|
class: 'h-4 w-4 flex-shrink-0 transition duration-300 group-hover:translate-x-1',
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
strokeWidth: '2',
|
strokeWidth: '2',
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
strokeLinejoin: 'round',
|
strokeLinejoin: 'round',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
facebook: {
|
facebook: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M9.101 23.691v-7.98H6.627v-3.667h2.474v-1.58c0-4.085 1.848-5.978 5.858-5.978.401 0 .955.042 1.468.103a8.68 8.68 0 0 1 1.141.195v3.325a8.623 8.623 0 0 0-.653-.036 26.805 26.805 0 0 0-.733-.009c-.707 0-1.259.096-1.675.309a1.686 1.686 0 0 0-.679.622c-.258.42-.374.995-.374 1.752v1.297h3.919l-.386 2.103-.287 1.564h-3.246v8.245C19.396 23.238 24 18.179 24 12.044c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.628 3.874 10.35 9.101 11.647Z',
|
d: 'M9.101 23.691v-7.98H6.627v-3.667h2.474v-1.58c0-4.085 1.848-5.978 5.858-5.978.401 0 .955.042 1.468.103a8.68 8.68 0 0 1 1.141.195v3.325a8.623 8.623 0 0 0-.653-.036 26.805 26.805 0 0 0-.733-.009c-.707 0-1.259.096-1.675.309a1.686 1.686 0 0 0-.679.622c-.258.42-.374.995-.374 1.752v1.297h3.919l-.386 2.103-.287 1.564h-3.246v8.245C19.396 23.238 24 18.179 24 12.044c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.628 3.874 10.35 9.101 11.647Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'size-4 flex-shrink-0 fill-current',
|
class: 'size-4 flex-shrink-0 fill-current',
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
x: {
|
x: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z',
|
d: 'M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'size-4 flex-shrink-0 fill-current',
|
class: 'size-4 flex-shrink-0 fill-current',
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
linkedIn: {
|
linkedIn: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z',
|
d: 'M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'size-4 flex-shrink-0 fill-current',
|
class: 'size-4 flex-shrink-0 fill-current',
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
share: {
|
share: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M7.217 10.907a2.25 2.25 0 1 0 0 2.186m0-2.186c.18.324.283.696.283 1.093s-.103.77-.283 1.093m0-2.186 9.566-5.314m-9.566 7.5 9.566 5.314m0 0a2.25 2.25 0 1 0 3.935 2.186 2.25 2.25 0 0 0-3.935-2.186Zm0-12.814a2.25 2.25 0 1 0 3.933-2.185 2.25 2.25 0 0 0-3.933 2.185Z',
|
d: 'M7.217 10.907a2.25 2.25 0 1 0 0 2.186m0-2.186c.18.324.283.696.283 1.093s-.103.77-.283 1.093m0-2.186 9.566-5.314m-9.566 7.5 9.566 5.314m0 0a2.25 2.25 0 1 0 3.935 2.186 2.25 2.25 0 0 0-3.935-2.186Zm0-12.814a2.25 2.25 0 1 0 3.933-2.185 2.25 2.25 0 0 0-3.933 2.185Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'h-4 w-4 group-hover:text-neutral-700',
|
class: 'h-4 w-4 group-hover:text-neutral-700',
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
strokeWidth: '1.5',
|
strokeWidth: '1.5',
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
strokeLinejoin: 'round',
|
strokeLinejoin: 'round',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
github: {
|
github: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z',
|
d: 'M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'w-4.5 h-4.5 transition flex-shrink-0 text-neutral-700 duration-300 group-hover:-translate-y-1',
|
class: 'w-4.5 h-4.5 transition flex-shrink-0 text-neutral-700 duration-300 group-hover:-translate-y-1',
|
||||||
width: 16,
|
width: 16,
|
||||||
height: 16,
|
height: 16,
|
||||||
viewBox: '0 0 16 16',
|
viewBox: '0 0 16 16',
|
||||||
fill: 'currentColor',
|
fill: 'currentColor',
|
||||||
},
|
},
|
||||||
arrowRightStatic: {
|
arrowRightStatic: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'm9 18 6-6-6-6',
|
d: 'm9 18 6-6-6-6',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'size-4 flex-shrink-0',
|
class: 'size-4 flex-shrink-0',
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
strokeWidth: '2',
|
strokeWidth: '2',
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
strokeLinejoin: 'round',
|
strokeLinejoin: 'round',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
openInNew: {
|
openInNew: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'm4.5 19.5 15-15m0 0H8.25m11.25 0v11.25',
|
d: 'm4.5 19.5 15-15m0 0H8.25m11.25 0v11.25',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'ml-0.5 w-3 h-3 md:w-4 md:h-4 inline pb-0.5',
|
class: 'ml-0.5 w-3 h-3 md:w-4 md:h-4 inline pb-0.5',
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
strokeWidth: '3',
|
strokeWidth: '3',
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
strokeLinejoin: 'round',
|
strokeLinejoin: 'round',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
accordionNotActive: {
|
accordionNotActive: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'm6 9 6 6 6-6',
|
d: 'm6 9 6 6 6-6',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'block h-5 w-5 flex-shrink-0 text-neutral-600 group-hover:text-neutral-500 hs-accordion-active:hidden dark:text-neutral-400',
|
class: 'block h-5 w-5 flex-shrink-0 text-neutral-600 group-hover:text-neutral-500 hs-accordion-active:hidden dark:text-neutral-400',
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
strokeWidth: '2',
|
strokeWidth: '2',
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
strokeLinejoin: 'round',
|
strokeLinejoin: 'round',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
accordionActive: {
|
accordionActive: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'm18 15-6-6-6 6',
|
d: 'm18 15-6-6-6 6',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'hidden h-5 w-5 flex-shrink-0 text-neutral-600 group-hover:text-neutral-500 hs-accordion-active:block dark:text-neutral-400',
|
class: 'hidden h-5 w-5 flex-shrink-0 text-neutral-600 group-hover:text-neutral-500 hs-accordion-active:block dark:text-neutral-400',
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
strokeWidth: '2',
|
strokeWidth: '2',
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
strokeLinejoin: 'round',
|
strokeLinejoin: 'round',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
xFooter: {
|
xFooter: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z',
|
d: 'M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'h-4 w-4 flex-shrink-0 fill-current text-neutral-700 dark:text-neutral-400',
|
class: 'h-4 w-4 flex-shrink-0 fill-current text-neutral-700 dark:text-neutral-400',
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'currentColor',
|
fill: 'currentColor',
|
||||||
title: 'Twitter',
|
title: 'Twitter',
|
||||||
},
|
},
|
||||||
facebookFooter: {
|
facebookFooter: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M9.101 23.691v-7.98H6.627v-3.667h2.474v-1.58c0-4.085 1.848-5.978 5.858-5.978.401 0 .955.042 1.468.103a8.68 8.68 0 0 1 1.141.195v3.325a8.623 8.623 0 0 0-.653-.036 26.805 26.805 0 0 0-.733-.009c-.707 0-1.259.096-1.675.309a1.686 1.686 0 0 0-.679.622c-.258.42-.374.995-.374 1.752v1.297h3.919l-.386 2.103-.287 1.564h-3.246v8.245C19.396 23.238 24 18.179 24 12.044c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.628 3.874 10.35 9.101 11.647Z',
|
d: 'M9.101 23.691v-7.98H6.627v-3.667h2.474v-1.58c0-4.085 1.848-5.978 5.858-5.978.401 0 .955.042 1.468.103a8.68 8.68 0 0 1 1.141.195v3.325a8.623 8.623 0 0 0-.653-.036 26.805 26.805 0 0 0-.733-.009c-.707 0-1.259.096-1.675.309a1.686 1.686 0 0 0-.679.622c-.258.42-.374.995-.374 1.752v1.297h3.919l-.386 2.103-.287 1.564h-3.246v8.245C19.396 23.238 24 18.179 24 12.044c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.628 3.874 10.35 9.101 11.647Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'h-4 w-4 flex-shrink-0 fill-current text-neutral-700 dark:text-neutral-400',
|
class: 'h-4 w-4 flex-shrink-0 fill-current text-neutral-700 dark:text-neutral-400',
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'currentColor',
|
fill: 'currentColor',
|
||||||
title: 'Facebook',
|
title: 'Facebook',
|
||||||
},
|
},
|
||||||
githubFooter: {
|
githubFooter: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12',
|
d: 'M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'h-4 w-4 flex-shrink-0 fill-current text-neutral-700 dark:text-neutral-400',
|
class: 'h-4 w-4 flex-shrink-0 fill-current text-neutral-700 dark:text-neutral-400',
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'currentColor',
|
fill: 'currentColor',
|
||||||
title: 'GitHub',
|
title: 'GitHub',
|
||||||
},
|
},
|
||||||
googleFooter: {
|
googleFooter: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z',
|
d: 'M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'h-4 w-4 flex-shrink-0 fill-current text-neutral-700 dark:text-neutral-400',
|
class: 'h-4 w-4 flex-shrink-0 fill-current text-neutral-700 dark:text-neutral-400',
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'currentColor',
|
fill: 'currentColor',
|
||||||
title: 'Google',
|
title: 'Google',
|
||||||
},
|
},
|
||||||
slackFooter: {
|
slackFooter: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z',
|
d: 'M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'h-4 w-4 flex-shrink-0 fill-current text-neutral-700 dark:text-neutral-400',
|
class: 'h-4 w-4 flex-shrink-0 fill-current text-neutral-700 dark:text-neutral-400',
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'currentColor',
|
fill: 'currentColor',
|
||||||
title: 'Slack',
|
title: 'Slack',
|
||||||
},
|
},
|
||||||
quotation: {
|
quotation: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M7.39762 10.3C7.39762 11.0733 7.14888 11.7 6.6514 12.18C6.15392 12.6333 5.52552 12.86 4.76621 12.86C3.84979 12.86 3.09047 12.5533 2.48825 11.94C1.91222 11.3266 1.62421 10.4467 1.62421 9.29999C1.62421 8.07332 1.96459 6.87332 2.64535 5.69999C3.35231 4.49999 4.33418 3.55332 5.59098 2.85999L6.4943 4.25999C5.81354 4.73999 5.26369 5.27332 4.84476 5.85999C4.45201 6.44666 4.19017 7.12666 4.05926 7.89999C4.29491 7.79332 4.56983 7.73999 4.88403 7.73999C5.61716 7.73999 6.21938 7.97999 6.69067 8.45999C7.16197 8.93999 7.39762 9.55333 7.39762 10.3ZM14.6242 10.3C14.6242 11.0733 14.3755 11.7 13.878 12.18C13.3805 12.6333 12.7521 12.86 11.9928 12.86C11.0764 12.86 10.3171 12.5533 9.71484 11.94C9.13881 11.3266 8.85079 10.4467 8.85079 9.29999C8.85079 8.07332 9.19117 6.87332 9.87194 5.69999C10.5789 4.49999 11.5608 3.55332 12.8176 2.85999L13.7209 4.25999C13.0401 4.73999 12.4903 5.27332 12.0713 5.85999C11.6786 6.44666 11.4168 7.12666 11.2858 7.89999C11.5215 7.79332 11.7964 7.73999 12.1106 7.73999C12.8437 7.73999 13.446 7.97999 13.9173 8.45999C14.3886 8.93999 14.6242 9.55333 14.6242 10.3Z',
|
d: 'M7.39762 10.3C7.39762 11.0733 7.14888 11.7 6.6514 12.18C6.15392 12.6333 5.52552 12.86 4.76621 12.86C3.84979 12.86 3.09047 12.5533 2.48825 11.94C1.91222 11.3266 1.62421 10.4467 1.62421 9.29999C1.62421 8.07332 1.96459 6.87332 2.64535 5.69999C3.35231 4.49999 4.33418 3.55332 5.59098 2.85999L6.4943 4.25999C5.81354 4.73999 5.26369 5.27332 4.84476 5.85999C4.45201 6.44666 4.19017 7.12666 4.05926 7.89999C4.29491 7.79332 4.56983 7.73999 4.88403 7.73999C5.61716 7.73999 6.21938 7.97999 6.69067 8.45999C7.16197 8.93999 7.39762 9.55333 7.39762 10.3ZM14.6242 10.3C14.6242 11.0733 14.3755 11.7 13.878 12.18C13.3805 12.6333 12.7521 12.86 11.9928 12.86C11.0764 12.86 10.3171 12.5533 9.71484 11.94C9.13881 11.3266 8.85079 10.4467 8.85079 9.29999C8.85079 8.07332 9.19117 6.87332 9.87194 5.69999C10.5789 4.49999 11.5608 3.55332 12.8176 2.85999L13.7209 4.25999C13.0401 4.73999 12.4903 5.27332 12.0713 5.85999C11.6786 6.44666 11.4168 7.12666 11.2858 7.89999C11.5215 7.79332 11.7964 7.73999 12.1106 7.73999C12.8437 7.73999 13.446 7.97999 13.9173 8.45999C14.3886 8.93999 14.6242 9.55333 14.6242 10.3Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'absolute start-0 top-0 h-16 w-16 -translate-x-6 -translate-y-8 transform text-neutral-300 dark:text-neutral-700',
|
class: 'absolute start-0 top-0 h-16 w-16 -translate-x-6 -translate-y-8 transform text-neutral-300 dark:text-neutral-700',
|
||||||
width: 16,
|
width: 16,
|
||||||
height: 16,
|
height: 16,
|
||||||
viewBox: '0 0 16 16',
|
viewBox: '0 0 16 16',
|
||||||
fill: 'currentColor',
|
fill: 'currentColor',
|
||||||
},
|
},
|
||||||
question: {
|
question: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z',
|
d: 'M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'mt-1.5 h-6 w-6 flex-shrink-0 text-neutral-600 dark:text-neutral-400',
|
class: 'mt-1.5 h-6 w-6 flex-shrink-0 text-neutral-600 dark:text-neutral-400',
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
strokeWidth: '1.5',
|
strokeWidth: '1.5',
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
strokeLinejoin: 'round',
|
strokeLinejoin: 'round',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
chatBubble: {
|
chatBubble: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 0 1-.825-.242m9.345-8.334a2.126 2.126 0 0 0-.476-.095 48.64 48.64 0 0 0-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0 0 11.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155',
|
d: 'M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 0 1-.825-.242m9.345-8.334a2.126 2.126 0 0 0-.476-.095 48.64 48.64 0 0 0-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0 0 11.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'mt-1.5 h-6 w-6 flex-shrink-0 text-neutral-600 dark:text-neutral-400',
|
class: 'mt-1.5 h-6 w-6 flex-shrink-0 text-neutral-600 dark:text-neutral-400',
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
strokeWidth: '1.5',
|
strokeWidth: '1.5',
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
strokeLinejoin: 'round',
|
strokeLinejoin: 'round',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
mapPin: {
|
mapPin: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z',
|
d: 'M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
d: 'M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z',
|
d: 'M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'mt-1.5 h-6 w-6 flex-shrink-0 text-neutral-600 dark:text-neutral-400',
|
class: 'mt-1.5 h-6 w-6 flex-shrink-0 text-neutral-600 dark:text-neutral-400',
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
strokeWidth: '1.5',
|
strokeWidth: '1.5',
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
strokeLinejoin: 'round',
|
strokeLinejoin: 'round',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
envelopeOpen: {
|
envelopeOpen: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'M21.75 9v.906a2.25 2.25 0 0 1-1.183 1.981l-6.478 3.488M2.25 9v.906a2.25 2.25 0 0 0 1.183 1.981l6.478 3.488m8.839 2.51-4.66-2.51m0 0-1.023-.55a2.25 2.25 0 0 0-2.134 0l-1.022.55m0 0-4.661 2.51m16.5 1.615a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V8.844a2.25 2.25 0 0 1 1.183-1.981l7.5-4.039a2.25 2.25 0 0 1 2.134 0l7.5 4.039a2.25 2.25 0 0 1 1.183 1.98V19.5Z',
|
d: 'M21.75 9v.906a2.25 2.25 0 0 1-1.183 1.981l-6.478 3.488M2.25 9v.906a2.25 2.25 0 0 0 1.183 1.981l6.478 3.488m8.839 2.51-4.66-2.51m0 0-1.023-.55a2.25 2.25 0 0 0-2.134 0l-1.022.55m0 0-4.661 2.51m16.5 1.615a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V8.844a2.25 2.25 0 0 1 1.183-1.981l7.5-4.039a2.25 2.25 0 0 1 2.134 0l7.5 4.039a2.25 2.25 0 0 1 1.183 1.98V19.5Z',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'mt-1.5 h-6 w-6 flex-shrink-0 text-neutral-600 dark:text-neutral-400',
|
class: 'mt-1.5 h-6 w-6 flex-shrink-0 text-neutral-600 dark:text-neutral-400',
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
strokeWidth: '1.5',
|
strokeWidth: '1.5',
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
strokeLinejoin: 'round',
|
strokeLinejoin: 'round',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
earth: {
|
earth: {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
d: 'm20.893 13.393-1.135-1.135a2.252 2.252 0 0 1-.421-.585l-1.08-2.16a.414.414 0 0 0-.663-.107.827.827 0 0 1-.812.21l-1.273-.363a.89.89 0 0 0-.738 1.595l.587.39c.59.395.674 1.23.172 1.732l-.2.2c-.212.212-.33.498-.33.796v.41c0 .409-.11.809-.32 1.158l-1.315 2.191a2.11 2.11 0 0 1-1.81 1.025 1.055 1.055 0 0 1-1.055-1.055v-1.172c0-.92-.56-1.747-1.414-2.089l-.655-.261a2.25 2.25 0 0 1-1.383-2.46l.007-.042a2.25 2.25 0 0 1 .29-.787l.09-.15a2.25 2.25 0 0 1 2.37-1.048l1.178.236a1.125 1.125 0 0 0 1.302-.795l.208-.73a1.125 1.125 0 0 0-.578-1.315l-.665-.332-.091.091a2.25 2.25 0 0 1-1.591.659h-.18c-.249 0-.487.1-.662.274a.931.931 0 0 1-1.458-1.137l1.411-2.353a2.25 2.25 0 0 0 .286-.76m11.928 9.869A9 9 0 0 0 8.965 3.525m11.928 9.868A9 9 0 1 1 8.965 3.525',
|
d: 'm20.893 13.393-1.135-1.135a2.252 2.252 0 0 1-.421-.585l-1.08-2.16a.414.414 0 0 0-.663-.107.827.827 0 0 1-.812.21l-1.273-.363a.89.89 0 0 0-.738 1.595l.587.39c.59.395.674 1.23.172 1.732l-.2.2c-.212.212-.33.498-.33.796v.41c0 .409-.11.809-.32 1.158l-1.315 2.191a2.11 2.11 0 0 1-1.81 1.025 1.055 1.055 0 0 1-1.055-1.055v-1.172c0-.92-.56-1.747-1.414-2.089l-.655-.261a2.25 2.25 0 0 1-1.383-2.46l.007-.042a2.25 2.25 0 0 1 .29-.787l.09-.15a2.25 2.25 0 0 1 2.37-1.048l1.178.236a1.125 1.125 0 0 0 1.302-.795l.208-.73a1.125 1.125 0 0 0-.578-1.315l-.665-.332-.091.091a2.25 2.25 0 0 1-1.591.659h-.18c-.249 0-.487.1-.662.274a.931.931 0 0 1-1.458-1.137l1.411-2.353a2.25 2.25 0 0 0 .286-.76m11.928 9.869A9 9 0 0 0 8.965 3.525m11.928 9.868A9 9 0 1 1 8.965 3.525',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
class: 'w-4 h-4 flex-shrink-0',
|
class: 'w-4 h-4 flex-shrink-0',
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
strokeWidth: '1.5',
|
strokeWidth: '1.5',
|
||||||
strokeLinecap: 'round',
|
strokeLinecap: 'round',
|
||||||
strokeLinejoin: 'round',
|
strokeLinejoin: 'round',
|
||||||
stroke: 'currentColor',
|
stroke: 'currentColor',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
---
|
---
|
||||||
import { Image } from 'astro:assets'
|
import { Image } from 'astro:assets'
|
||||||
import logo from '@images/brand-logo.jpeg'
|
import logo from '@images/brand-logo.jpeg'
|
||||||
import { SITE } from '../../config';
|
import { SITE } from '../../config';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src={logo}
|
src={logo}
|
||||||
alt={SITE.title}
|
alt={SITE.title}
|
||||||
{...Astro.props}
|
{...Astro.props}
|
||||||
draggable="false"
|
draggable="false"
|
||||||
loading="eager"
|
loading="eager"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -1,48 +1,48 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="Dark Theme Toggle"
|
aria-label="Dark Theme Toggle"
|
||||||
class="hs-dark-mode group flex h-8 w-8 items-center justify-center rounded-full font-medium text-neutral-600 outline-none ring-zinc-500 transition duration-300 hover:bg-neutral-200 hover:text-orange-400 hs-dark-mode-active:hidden dark:text-neutral-400 dark:ring-zinc-200 dark:hover:text-orange-300 dark:focus:outline-none"
|
class="hs-dark-mode group flex h-8 w-8 items-center justify-center rounded-full font-medium text-neutral-600 outline-none ring-zinc-500 transition duration-300 hover:bg-neutral-200 hover:text-orange-400 hs-dark-mode-active:hidden dark:text-neutral-400 dark:ring-zinc-200 dark:hover:text-orange-300 dark:focus:outline-none"
|
||||||
data-hs-theme-click-value="dark"
|
data-hs-theme-click-value="dark"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="h-4 w-4 flex-shrink-0"
|
class="h-4 w-4 flex-shrink-0"
|
||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path>
|
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="Light Theme Toggle"
|
aria-label="Light Theme Toggle"
|
||||||
class="hs-dark-mode group hidden h-8 w-8 items-center justify-center rounded-full font-medium text-neutral-600 outline-none ring-zinc-500 transition duration-300 hover:text-orange-400 hs-dark-mode-active:flex dark:text-neutral-400 dark:ring-zinc-200 dark:hover:bg-neutral-700 dark:hover:text-orange-300 dark:focus:outline-none"
|
class="hs-dark-mode group hidden h-8 w-8 items-center justify-center rounded-full font-medium text-neutral-600 outline-none ring-zinc-500 transition duration-300 hover:text-orange-400 hs-dark-mode-active:flex dark:text-neutral-400 dark:ring-zinc-200 dark:hover:bg-neutral-700 dark:hover:text-orange-300 dark:focus:outline-none"
|
||||||
data-hs-theme-click-value="light"
|
data-hs-theme-click-value="light"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="h-4 w-4 flex-shrink-0"
|
class="h-4 w-4 flex-shrink-0"
|
||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<circle cx="12" cy="12" r="4"></circle>
|
<circle cx="12" cy="12" r="4"></circle>
|
||||||
<path d="M12 8a2 2 0 1 0 4 4"></path>
|
<path d="M12 8a2 2 0 1 0 4 4"></path>
|
||||||
<path d="M12 2v2"></path>
|
<path d="M12 2v2"></path>
|
||||||
<path d="M12 20v2"></path>
|
<path d="M12 20v2"></path>
|
||||||
<path d="m4.93 4.93 1.41 1.41"></path>
|
<path d="m4.93 4.93 1.41 1.41"></path>
|
||||||
<path d="m17.66 17.66 1.41 1.41"></path>
|
<path d="m17.66 17.66 1.41 1.41"></path>
|
||||||
<path d="M2 12h2"></path>
|
<path d="M2 12h2"></path>
|
||||||
<path d="M20 12h2"></path><path d="m6.34 17.66-1.41 1.41"></path>
|
<path d="M20 12h2"></path><path d="m6.34 17.66-1.41 1.41"></path>
|
||||||
<path d="m19.07 4.93-1.41 1.41"></path>
|
<path d="m19.07 4.93-1.41 1.41"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
---
|
---
|
||||||
import { GoogleAnalytics } from '../../config'
|
import { GoogleAnalytics } from '../../config'
|
||||||
|
|
||||||
const ID = GoogleAnalytics.id
|
const ID = GoogleAnalytics.id
|
||||||
---
|
---
|
||||||
|
|
||||||
{
|
{
|
||||||
GoogleAnalytics.enable && (
|
GoogleAnalytics.enable && (
|
||||||
<script is:inline type="text/partytown" src=`https://www.googletagmanager.com/gtag/js?id=${ID}`></script>
|
<script is:inline type="text/partytown" src=`https://www.googletagmanager.com/gtag/js?id=${ID}`></script>
|
||||||
<script is:inline define:vars={{ ID }}>
|
<script is:inline define:vars={{ ID }}>
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
window.gtag = function gtag(){dataLayer.push(arguments);}
|
window.gtag = function gtag(){dataLayer.push(arguments);}
|
||||||
window.gtag('js', new Date());
|
window.gtag('js', new Date());
|
||||||
window.gtag('config', ID);
|
window.gtag('config', ID);
|
||||||
</script>
|
</script>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
import { type CollectionKey, getEntry } from 'astro:content'
|
import { type CollectionKey, getEntry } from 'astro:content'
|
||||||
|
|
||||||
const { collection, slug } = Astro.params
|
const { collection, slug } = Astro.params
|
||||||
const entry = await getEntry(collection as CollectionKey, slug)
|
const entry = await getEntry(collection as CollectionKey, slug)
|
||||||
const { Content } = await entry.render();
|
const { Content } = await entry.render();
|
||||||
---
|
---
|
||||||
|
|
||||||
<Content />
|
<Content />
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
---
|
---
|
||||||
const { url } = Astro.props
|
const { url } = Astro.props
|
||||||
interface Props {
|
interface Props {
|
||||||
url: string
|
url: string
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<a
|
<a
|
||||||
class="inline-flex h-14 w-14 items-center justify-center gap-x-2 rounded-lg border border-transparent text-sm font-bold text-neutral-700 outline-none ring-zinc-500 hover:bg-neutral-500/10 focus:outline-none focus-visible:ring focus-visible:ring-zinc-500 dark:ring-zinc-200 dark:hover:bg-neutral-50/10"
|
class="inline-flex h-14 w-14 items-center justify-center gap-x-2 rounded-lg border border-transparent text-sm font-bold text-neutral-700 outline-none ring-zinc-500 hover:bg-neutral-500/10 focus:outline-none focus-visible:ring focus-visible:ring-zinc-500 dark:ring-zinc-200 dark:hover:bg-neutral-50/10"
|
||||||
href={url}
|
href={url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,44 +1,44 @@
|
|||||||
---
|
---
|
||||||
const { url, name } = Astro.props
|
const { url, name } = Astro.props
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
url: string
|
url: string
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<a
|
<a
|
||||||
id={url === '/' ? 'home' : url.replace('/', '')}
|
id={url === '/' ? 'home' : url.replace('/', '')}
|
||||||
href={url}
|
href={url}
|
||||||
data-astro-prefetch
|
data-astro-prefetch
|
||||||
class="rounded-lg text-base font-medium text-neutral-600 outline-none ring-zinc-500 hover:text-neutral-500 focus-visible:ring dark:text-neutral-400 dark:ring-zinc-200 dark:hover:text-neutral-500 dark:focus:outline-none md:py-3 md:text-sm 2xl:text-base"
|
class="rounded-lg text-base font-medium text-neutral-600 outline-none ring-zinc-500 hover:text-neutral-500 focus-visible:ring dark:text-neutral-400 dark:ring-zinc-200 dark:hover:text-neutral-500 dark:focus:outline-none md:py-3 md:text-sm 2xl:text-base"
|
||||||
>
|
>
|
||||||
{name}
|
{name}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
let url = window.location.pathname;
|
let url = window.location.pathname;
|
||||||
let urlSegments = url.split("/");
|
let urlSegments = url.split("/");
|
||||||
let navId;
|
let navId;
|
||||||
|
|
||||||
if (url === "/") {
|
if (url === "/") {
|
||||||
navId = "home";
|
navId = "home";
|
||||||
} else {
|
} else {
|
||||||
navId = url.replace("/", "");
|
navId = url.replace("/", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
let nav = document.getElementById(navId);
|
let nav = document.getElementById(navId);
|
||||||
|
|
||||||
if (nav) {
|
if (nav) {
|
||||||
nav.classList.remove(
|
nav.classList.remove(
|
||||||
"text-neutral-600",
|
"text-neutral-600",
|
||||||
"dark:text-neutral-400",
|
"dark:text-neutral-400",
|
||||||
"hover:text-neutral-500",
|
"hover:text-neutral-500",
|
||||||
"dark:hover:text-neutral-500"
|
"dark:hover:text-neutral-500"
|
||||||
);
|
);
|
||||||
nav.classList.add("text-orange-400", "dark:text-orange-300");
|
nav.classList.add("text-orange-400", "dark:text-orange-300");
|
||||||
nav.setAttribute("aria-current", "page");
|
nav.setAttribute("aria-current", "page");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
---
|
---
|
||||||
title: 'Книги для прочтения 🔖'
|
title: 'Книги для прочтения 🔖'
|
||||||
description: 'Делимся бибилотекой'
|
description: 'Делимся бибилотекой'
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
---
|
---
|
||||||
title: 'Жизнь компании'
|
title: 'Жизнь компании'
|
||||||
description: 'Описание кто мы и что мы'
|
description: 'Описание кто мы и что мы'
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
---
|
---
|
||||||
title: 'Наша подъемная лестница 🪜'
|
title: 'Наша подъемная лестница 🪜'
|
||||||
description: 'Вверх по ступенькам'
|
description: 'Вверх по ступенькам'
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
import { defineCollection, z } from 'astro:content'
|
import { defineCollection, z } from 'astro:content'
|
||||||
|
|
||||||
const posts = defineCollection({
|
const posts = defineCollection({
|
||||||
schema: ({ image }) => z.object({
|
schema: ({ image }) => z.object({
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
banner: image(),
|
banner: image(),
|
||||||
|
|
||||||
// This banner will be shown in blog lists(/posts) if provided.
|
// This banner will be shown in blog lists(/posts) if provided.
|
||||||
banner2: image().optional(),
|
banner2: image().optional(),
|
||||||
|
|
||||||
// The article OG cover, if not provided, use summary card, otherwise summary_large_image
|
// The article OG cover, if not provided, use summary card, otherwise summary_large_image
|
||||||
ogImage: image().refine(img => img.width >= 1200 && img.height >= 630, {
|
ogImage: image().refine(img => img.width >= 1200 && img.height >= 630, {
|
||||||
message: 'OpenGraph image must be at least 1200 X 630 pixels!',
|
message: 'OpenGraph image must be at least 1200 X 630 pixels!',
|
||||||
}).or(z.string()).optional(),
|
}).or(z.string()).optional(),
|
||||||
|
|
||||||
category: z.string(),
|
category: z.string(),
|
||||||
pubDate: z.coerce.date(),
|
pubDate: z.coerce.date(),
|
||||||
|
|
||||||
// Should the article be added to SELECTED POSTS? will be displayed on the /posts page if true.
|
// Should the article be added to SELECTED POSTS? will be displayed on the /posts page if true.
|
||||||
selected: z.boolean().optional(),
|
selected: z.boolean().optional(),
|
||||||
|
|
||||||
tags: z.array(z.string()).optional(),
|
tags: z.array(z.string()).optional(),
|
||||||
|
|
||||||
// not use, just record this value since its from my previous blog system
|
// not use, just record this value since its from my previous blog system
|
||||||
updatedDate: z.coerce.date().optional(),
|
updatedDate: z.coerce.date().optional(),
|
||||||
oldViewCount: z.number().optional(),
|
oldViewCount: z.number().optional(),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
const categoryCollection = defineCollection({
|
const categoryCollection = defineCollection({
|
||||||
type: 'content',
|
type: 'content',
|
||||||
schema: () => z.object({
|
schema: () => z.object({
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const collections = { posts, categories: categoryCollection }
|
export const collections = { posts, categories: categoryCollection }
|
||||||
|
|||||||
@@ -1,42 +1,42 @@
|
|||||||
---
|
---
|
||||||
title: "ООО НПО Коралл-К"
|
title: "ООО НПО Коралл-К"
|
||||||
description: "Настоящим сообщаем, что наша компания готова оказать профессиональные услуги по разработке и внедрению системы ХАССП (Hazard Analysis and Critical Control Points)"
|
description: "Настоящим сообщаем, что наша компания готова оказать профессиональные услуги по разработке и внедрению системы ХАССП (Hazard Analysis and Critical Control Points)"
|
||||||
pubDate: "2024-06-09 15:10:51"
|
pubDate: "2024-06-09 15:10:51"
|
||||||
category: "life"
|
category: "life"
|
||||||
banner: "@images/banners/23zdmLzhMv4Y6HhpqSTmupMhL6CFQjIEewpAAuYL.jpeg"
|
banner: "@images/banners/23zdmLzhMv4Y6HhpqSTmupMhL6CFQjIEewpAAuYL.jpeg"
|
||||||
banner2: "@images/banners/review-2023.png"
|
banner2: "@images/banners/review-2023.png"
|
||||||
tags: ["life"]
|
tags: ["life"]
|
||||||
oldViewCount: 754
|
oldViewCount: 754
|
||||||
selected: true
|
selected: true
|
||||||
oldKeywords: ["2024"]
|
oldKeywords: ["2024"]
|
||||||
---
|
---
|
||||||
|
|
||||||
# Уважаемые господа!
|
# Уважаемые господа!
|
||||||
Внедрение системы ХАССП (Hazard Analysis and Critical Control Points) началось в странах Европейского Союза с Директивы Совета Европы по гигиене продуктов питания № 93/43/ЕС от 14 июня 1993 года. Эта директива обязывает компании, занятые в пищевой промышленности, разрабатывать системы, основанные на принципах ХАССП, с целью обеспечения безопасности пищевой продукции. В 2004 году на смену данной директиве пришло Постановление Европейского парламента и Совета Европы 852/2004 «О санитарно-гигиенических правилах производства пищевых продуктов».
|
Внедрение системы ХАССП (Hazard Analysis and Critical Control Points) началось в странах Европейского Союза с Директивы Совета Европы по гигиене продуктов питания № 93/43/ЕС от 14 июня 1993 года. Эта директива обязывает компании, занятые в пищевой промышленности, разрабатывать системы, основанные на принципах ХАССП, с целью обеспечения безопасности пищевой продукции. В 2004 году на смену данной директиве пришло Постановление Европейского парламента и Совета Европы 852/2004 «О санитарно-гигиенических правилах производства пищевых продуктов».
|
||||||
|
|
||||||
## Стоимость услуг:
|
## Стоимость услуг:
|
||||||
- Разработка и внедрение системы ХАССП для одного пищевого объекта (столовой) – 80 000 рублей (без НДС).
|
- Разработка и внедрение системы ХАССП для одного пищевого объекта (столовой) – 80 000 рублей (без НДС).
|
||||||
- Срок выполнения работы – 2 месяца.
|
- Срок выполнения работы – 2 месяца.
|
||||||
|
|
||||||
## Дополнительно:
|
## Дополнительно:
|
||||||
-Обучение сотрудников по работе с документацией системы ХАССП (индивидуально или групповые занятия, с выездом или онлайн). Стоимость рассчитывается индивидуально, от 15 000 рублей за одного сотрудника.
|
-Обучение сотрудников по работе с документацией системы ХАССП (индивидуально или групповые занятия, с выездом или онлайн). Стоимость рассчитывается индивидуально, от 15 000 рублей за одного сотрудника.
|
||||||
-Документальное подтверждение прохождения обучения на внутренних аудиторов системы ХАССП.
|
-Документальное подтверждение прохождения обучения на внутренних аудиторов системы ХАССП.
|
||||||
|
|
||||||
Отсутствие разработанной и внедренной системы ХАССП влечет административную ответственность согласно ч. 1 ст. 14.43 Кодекса Российской Федерации об административных правонарушениях от 30.12.2001 № 195-ФЗ. Штрафы составляют:
|
Отсутствие разработанной и внедренной системы ХАССП влечет административную ответственность согласно ч. 1 ст. 14.43 Кодекса Российской Федерации об административных правонарушениях от 30.12.2001 № 195-ФЗ. Штрафы составляют:
|
||||||
|
|
||||||
- Для граждан – от 1 000 до 2 000 рублей;
|
- Для граждан – от 1 000 до 2 000 рублей;
|
||||||
- Для должностных лиц – от 10 000 до 20 000 рублей;
|
- Для должностных лиц – от 10 000 до 20 000 рублей;
|
||||||
- Для лиц, осуществляющих предпринимательскую деятельность без образования юридического лица – от 20 000 до 30 000 рублей;
|
- Для лиц, осуществляющих предпринимательскую деятельность без образования юридического лица – от 20 000 до 30 000 рублей;
|
||||||
- Для юридических лиц – от 100 000 до 300 000 рублей.
|
- Для юридических лиц – от 100 000 до 300 000 рублей.
|
||||||
|
|
||||||
Кроме уплаты штрафа, виновное лицо обязано устранить нарушение, разработав и внедрив систему ХАССП.
|
Кроме уплаты штрафа, виновное лицо обязано устранить нарушение, разработав и внедрив систему ХАССП.
|
||||||
|
|
||||||
## Преимущества внедрения системы ХАССП:
|
## Преимущества внедрения системы ХАССП:
|
||||||
- Систематизация контроля качества на всех этапах производства;
|
- Систематизация контроля качества на всех этапах производства;
|
||||||
- Определение ответственных за безопасность продукции;
|
- Определение ответственных за безопасность продукции;
|
||||||
- Эффективное выявление и устранение рисков;
|
- Эффективное выявление и устранение рисков;
|
||||||
- Своевременное обнаружение и ликвидация опасностей;
|
- Своевременное обнаружение и ликвидация опасностей;
|
||||||
- Документальное подтверждение безопасности продукции, что важно в судебных разбирательствах.
|
- Документальное подтверждение безопасности продукции, что важно в судебных разбирательствах.
|
||||||
|
|
||||||
##
|
##
|
||||||
ООО НПО «Коралл-К» занимается разработкой в области прикладной экологии с 1993 года и имеет штат высококвалифицированных сотрудников, а также необходимое оборудование и программное обеспечение. За время работы мы разработали и согласовали более 5000 проектов в области охраны окружающей среды. Наши заказчики: Иркутский авиационный завод (филиал ПАО «Корпорация Иркут»), АО «Дорожная служба Иркутской области», ОАО «РЖД», Центральный банк Российской Федерации, ФГУП «Почта России», ОАО «Областное жилищно-коммунальное хозяйство», ОАО «Высочайший», ОГАУСО «Комплексный центр социального обслуживания населения».
|
ООО НПО «Коралл-К» занимается разработкой в области прикладной экологии с 1993 года и имеет штат высококвалифицированных сотрудников, а также необходимое оборудование и программное обеспечение. За время работы мы разработали и согласовали более 5000 проектов в области охраны окружающей среды. Наши заказчики: Иркутский авиационный завод (филиал ПАО «Корпорация Иркут»), АО «Дорожная служба Иркутской области», ОАО «РЖД», Центральный банк Российской Федерации, ФГУП «Почта России», ОАО «Областное жилищно-коммунальное хозяйство», ОАО «Высочайший», ОГАУСО «Комплексный центр социального обслуживания населения».
|
||||||
@@ -1,58 +1,58 @@
|
|||||||
---
|
---
|
||||||
title: "Оказание методической помощи"
|
title: "Оказание методической помощи"
|
||||||
description: "Оказание методической помощи в реализации процессов производства пищевой продукции на основе принципов ХАССП"
|
description: "Оказание методической помощи в реализации процессов производства пищевой продукции на основе принципов ХАССП"
|
||||||
pubDate: "2024-06-09 15:10:51"
|
pubDate: "2024-06-09 15:10:51"
|
||||||
category: "life"
|
category: "life"
|
||||||
banner: "@images/banners/23zdmLzhMv4Y6HhpqSTmupMhL6CFQjIEewpAAuYL.jpeg"
|
banner: "@images/banners/23zdmLzhMv4Y6HhpqSTmupMhL6CFQjIEewpAAuYL.jpeg"
|
||||||
banner2: "@images/banners/review-2023.png"
|
banner2: "@images/banners/review-2023.png"
|
||||||
tags: ["life"]
|
tags: ["life"]
|
||||||
oldViewCount: 754
|
oldViewCount: 754
|
||||||
selected: true
|
selected: true
|
||||||
oldKeywords: ["2024"]
|
oldKeywords: ["2024"]
|
||||||
---
|
---
|
||||||
|
|
||||||
#
|
#
|
||||||
Внедрение системы ХАССП (Hazard Analysis and Critical Control Points) началось в странах Европейского Союза с Директивы Совета Европы по гигиене продуктов питания № 93/43/ЕС от 14 июня 1993 года. Эта директива обязывает компании, занятые в пищевой промышленности, разрабатывать системы, основанные на принципах ХАССП, с целью обеспечения безопасности пищевой продукции. В 2004 году на смену данной директиве пришло Постановление Европейского парламента и Совета Европы 852/2004 «О санитарно-гигиенических правилах производства пищевых продуктов».
|
Внедрение системы ХАССП (Hazard Analysis and Critical Control Points) началось в странах Европейского Союза с Директивы Совета Европы по гигиене продуктов питания № 93/43/ЕС от 14 июня 1993 года. Эта директива обязывает компании, занятые в пищевой промышленности, разрабатывать системы, основанные на принципах ХАССП, с целью обеспечения безопасности пищевой продукции. В 2004 году на смену данной директиве пришло Постановление Европейского парламента и Совета Европы 852/2004 «О санитарно-гигиенических правилах производства пищевых продуктов».
|
||||||
|
|
||||||
##
|
##
|
||||||
Согласно п. 2.1 СанПиН 2.3/2.4.3590-20 «Санитарно-эпидемиологические требования к организации общественного питания населения», утвержденного постановлением Главного государственного санитарного врача Российской Федерации № 32 от 27.10.2020 года, зарегистрированного Министерством юстиции РФ № 60833 от 11.11.2020 года, все предприятия общественного питания обязаны проводить производственный контроль, основанный на принципах ХАССП.
|
Согласно п. 2.1 СанПиН 2.3/2.4.3590-20 «Санитарно-эпидемиологические требования к организации общественного питания населения», утвержденного постановлением Главного государственного санитарного врача Российской Федерации № 32 от 27.10.2020 года, зарегистрированного Министерством юстиции РФ № 60833 от 11.11.2020 года, все предприятия общественного питания обязаны проводить производственный контроль, основанный на принципах ХАССП.
|
||||||
|
|
||||||
## Цели внедрения ХАССП:
|
## Цели внедрения ХАССП:
|
||||||
- Защита жизни и здоровья человека.
|
- Защита жизни и здоровья человека.
|
||||||
- Предупреждение действий, вводящих в заблуждение потребителей.
|
- Предупреждение действий, вводящих в заблуждение потребителей.
|
||||||
|
|
||||||
## Требования к предприятиям:
|
## Требования к предприятиям:
|
||||||
- Изготовители, продавцы и лица, выполняющие функции иностранных изготовителей, обязаны соблюдать процессы производства, хранения и реализации пищевой продукции в соответствии с требованиями ТР ТС 021/2011 и других технических регламентов Таможенного союза.
|
- Изготовители, продавцы и лица, выполняющие функции иностранных изготовителей, обязаны соблюдать процессы производства, хранения и реализации пищевой продукции в соответствии с требованиями ТР ТС 021/2011 и других технических регламентов Таможенного союза.
|
||||||
- Разработка, внедрение и поддержка процедур, основанных на принципах ХАССП.
|
- Разработка, внедрение и поддержка процедур, основанных на принципах ХАССП.
|
||||||
|
|
||||||
## Основные процедуры для обеспечения безопасности пищевой продукции:
|
## Основные процедуры для обеспечения безопасности пищевой продукции:
|
||||||
- Выбор необходимых технологических процессов.
|
- Выбор необходимых технологических процессов.
|
||||||
- Выбор последовательности и поточности технологических операций.
|
- Выбор последовательности и поточности технологических операций.
|
||||||
- Определение контролируемых этапов производства в программах производственного контроля.
|
- Определение контролируемых этапов производства в программах производственного контроля.
|
||||||
- Контроль за сырьем, технологическими средствами и упаковочными материалами.
|
- Контроль за сырьем, технологическими средствами и упаковочными материалами.
|
||||||
- Контроль за функционированием технологического оборудования.
|
- Контроль за функционированием технологического оборудования.
|
||||||
- Документирование контролируемых этапов и результатов контроля.
|
- Документирование контролируемых этапов и результатов контроля.
|
||||||
- Соблюдение условий хранения и перевозки.
|
- Соблюдение условий хранения и перевозки.
|
||||||
- Содержание производственных помещений и оборудования в состоянии, исключающем загрязнение продукции.
|
- Содержание производственных помещений и оборудования в состоянии, исключающем загрязнение продукции.
|
||||||
- Обеспечение соблюдения правил личной гигиены.
|
- Обеспечение соблюдения правил личной гигиены.
|
||||||
- Уборка, мойка, дезинфекция, дезинсекция и дератизация производственных помещений и оборудования.
|
- Уборка, мойка, дезинфекция, дезинсекция и дератизация производственных помещений и оборудования.
|
||||||
- Ведение документации, подтверждающей соответствие продукции требованиям.
|
- Ведение документации, подтверждающей соответствие продукции требованиям.
|
||||||
- Обеспечение прослеживаемости пищевой продукции.
|
- Обеспечение прослеживаемости пищевой продукции.
|
||||||
|
|
||||||
## Ответственность за несоблюдение ХАССП:
|
## Ответственность за несоблюдение ХАССП:
|
||||||
Согласно ч. 1 ст. 14.43 Кодекса Российской Федерации об административных правонарушениях, нарушение требований технических регламентов влечет наложение административного штрафа:
|
Согласно ч. 1 ст. 14.43 Кодекса Российской Федерации об административных правонарушениях, нарушение требований технических регламентов влечет наложение административного штрафа:
|
||||||
|
|
||||||
- На граждан – от 1 000 до 2 000 рублей.
|
- На граждан – от 1 000 до 2 000 рублей.
|
||||||
- На должностных лиц – от 10 000 до 20 000 рублей.
|
- На должностных лиц – от 10 000 до 20 000 рублей.
|
||||||
- На индивидуальных предпринимателей – от 20 000 до 30 000 рублей.
|
- На индивидуальных предпринимателей – от 20 000 до 30 000 рублей.
|
||||||
- На юридических лиц – от 100 000 до 300 000 рублей.
|
- На юридических лиц – от 100 000 до 300 000 рублей.
|
||||||
|
|
||||||
После уплаты штрафа виновное лицо обязано устранить нарушение путем разработки и внедрения системы ХАССП.
|
После уплаты штрафа виновное лицо обязано устранить нарушение путем разработки и внедрения системы ХАССП.
|
||||||
|
|
||||||
## Преимущества системы ХАССП для учреждений:
|
## Преимущества системы ХАССП для учреждений:
|
||||||
|
|
||||||
- Систематизация контроля качества на всех этапах производства.
|
- Систематизация контроля качества на всех этапах производства.
|
||||||
- Определение ответственных за обеспечение безопасности продукции.
|
- Определение ответственных за обеспечение безопасности продукции.
|
||||||
- Эффективное выявление и устранение рисков.
|
- Эффективное выявление и устранение рисков.
|
||||||
- Своевременное выявление опасностей и их оперативное устранение.
|
- Своевременное выявление опасностей и их оперативное устранение.
|
||||||
- Документальное подтверждение безопасности продукции, что важно в случаях судебных разбирательств.
|
- Документальное подтверждение безопасности продукции, что важно в случаях судебных разбирательств.
|
||||||
2
src/env.d.ts
vendored
2
src/env.d.ts
vendored
@@ -1,2 +1,2 @@
|
|||||||
/// <reference types="astro/client" />
|
/// <reference types="astro/client" />
|
||||||
/// <reference path="../.astro/types.d.ts" />
|
/// <reference path="../.astro/types.d.ts" />
|
||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
@@ -1,84 +1,84 @@
|
|||||||
---
|
---
|
||||||
import BaseHead from '@components/BaseHead.astro'
|
import BaseHead from '@components/BaseHead.astro'
|
||||||
import Footer from '@components/Footer.astro'
|
import Footer from '@components/Footer.astro'
|
||||||
import Header from '@components/Header.astro'
|
import Header from '@components/Header.astro'
|
||||||
import GoogleAnalytics from '@components/support/GoogleAnalytics.astro'
|
import GoogleAnalytics from '@components/support/GoogleAnalytics.astro'
|
||||||
import { SITE } from '../config'
|
import { SITE } from '../config'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
ogImage?: any
|
ogImage?: any
|
||||||
lang?: string
|
lang?: string
|
||||||
structuredData?: object
|
structuredData?: object
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title, description = SITE.description, ogImage, lang = 'en', structuredData } = Astro.props
|
const { title, description = SITE.description, ogImage, lang = 'en', structuredData } = Astro.props
|
||||||
const normalizeTitle = !title ? SITE.title : `${title} | ${SITE.title}`
|
const normalizeTitle = !title ? SITE.title : `${title} | ${SITE.title}`
|
||||||
---
|
---
|
||||||
|
|
||||||
<html lang={lang} class="scrollbar-hide lenis lenis-smooth scroll-pt-16">
|
<html lang={lang} class="scrollbar-hide lenis lenis-smooth scroll-pt-16">
|
||||||
<head>
|
<head>
|
||||||
<title>{normalizeTitle}</title>
|
<title>{normalizeTitle}</title>
|
||||||
<BaseHead
|
<BaseHead
|
||||||
title={normalizeTitle}
|
title={normalizeTitle}
|
||||||
description={description}
|
description={description}
|
||||||
ogImage={ogImage}
|
ogImage={ogImage}
|
||||||
ogTitle={title === '' ? SITE.title : title}
|
ogTitle={title === '' ? SITE.title : title}
|
||||||
ogDescription={description}
|
ogDescription={description}
|
||||||
structuredData={structuredData}
|
structuredData={structuredData}
|
||||||
/>
|
/>
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
if (localStorage.getItem("hs_theme") === "dark"
|
if (localStorage.getItem("hs_theme") === "dark"
|
||||||
|| (!("hs_theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
|
|| (!("hs_theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
|
||||||
document.documentElement.classList.add("dark");
|
document.documentElement.classList.add("dark");
|
||||||
} else {
|
} else {
|
||||||
document.documentElement.classList.remove("dark");
|
document.documentElement.classList.remove("dark");
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script is:inline src="/vendor/lenis/lenis1.0.42.min.js"></script>
|
<script is:inline src="/vendor/lenis/lenis1.0.42.min.js"></script>
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
const lenis = new Lenis();
|
const lenis = new Lenis();
|
||||||
function raf(time) {
|
function raf(time) {
|
||||||
lenis.raf(time)
|
lenis.raf(time)
|
||||||
requestAnimationFrame(raf)
|
requestAnimationFrame(raf)
|
||||||
}
|
}
|
||||||
requestAnimationFrame(raf)
|
requestAnimationFrame(raf)
|
||||||
</script>
|
</script>
|
||||||
<GoogleAnalytics />
|
<GoogleAnalytics />
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-neutral-200 selection:bg-yellow-400 selection:text-neutral-700 dark:bg-neutral-800">
|
<body class="bg-neutral-200 selection:bg-yellow-400 selection:text-neutral-700 dark:bg-neutral-800">
|
||||||
<div class="mx-auto max-w-screen-2xl px-4 sm:px-6 lg:px-8">
|
<div class="mx-auto max-w-screen-2xl px-4 sm:px-6 lg:px-8">
|
||||||
<Header />
|
<Header />
|
||||||
<main>
|
<main>
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
<style>
|
<style>
|
||||||
.scrollbar-hide::-webkit-scrollbar {
|
.scrollbar-hide::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.scrollbar-hide {
|
.scrollbar-hide {
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
}
|
}
|
||||||
html.lenis,
|
html.lenis,
|
||||||
html.lenis body {
|
html.lenis body {
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
.lenis.lenis-smooth {
|
.lenis.lenis-smooth {
|
||||||
scroll-behavior: auto !important;
|
scroll-behavior: auto !important;
|
||||||
}
|
}
|
||||||
.lenis.lenis-smooth [data-lenis-prevent] {
|
.lenis.lenis-smooth [data-lenis-prevent] {
|
||||||
overscroll-behavior: contain;
|
overscroll-behavior: contain;
|
||||||
}
|
}
|
||||||
.lenis.lenis-stopped {
|
.lenis.lenis-stopped {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.lenis.lenis-scrolling iframe {
|
.lenis.lenis-scrolling iframe {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from '@layouts/BaseLayout.astro'
|
import BaseLayout from '@layouts/BaseLayout.astro'
|
||||||
import PrimaryCTA from '@components/buttons/PrimaryCTA.astro'
|
import PrimaryCTA from '@components/buttons/PrimaryCTA.astro'
|
||||||
import { SITE } from '../config'
|
import { SITE } from '../config'
|
||||||
|
|
||||||
const pageTitle: string = `Page Not Found | ${SITE.title}`
|
const pageTitle: string = `Page Not Found | ${SITE.title}`
|
||||||
|
|
||||||
const title: string = 'Not Found'
|
const title: string = 'Not Found'
|
||||||
const subTitle: string = '我们真的尽力了,但还是没找到您要的内容'
|
const subTitle: string = '我们真的尽力了,但还是没找到您要的内容'
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={pageTitle}>
|
<BaseLayout title={pageTitle}>
|
||||||
<section class="grid h-svh place-content-center">
|
<section class="grid h-svh place-content-center">
|
||||||
<div class="mx-auto max-w-screen-xl px-4 py-8 lg:px-6 lg:py-16">
|
<div class="mx-auto max-w-screen-xl px-4 py-8 lg:px-6 lg:py-16">
|
||||||
<div class="mx-auto max-w-screen-sm text-center">
|
<div class="mx-auto max-w-screen-sm text-center">
|
||||||
<h1 class="text-dark mb-4 text-7xl font-extrabold text-yellow-500 dark:text-yellow-400 lg:text-9xl">
|
<h1 class="text-dark mb-4 text-7xl font-extrabold text-yellow-500 dark:text-yellow-400 lg:text-9xl">
|
||||||
{title}
|
{title}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="mb-4 text-balance text-3xl font-bold tracking-tight text-neutral-700 dark:text-neutral-300 md:text-4xl">
|
<p class="mb-4 text-balance text-3xl font-bold tracking-tight text-neutral-700 dark:text-neutral-300 md:text-4xl">
|
||||||
{subTitle}
|
{subTitle}
|
||||||
</p>
|
</p>
|
||||||
<PrimaryCTA title="Home" url="/" />
|
<PrimaryCTA title="Home" url="/" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|||||||
@@ -1,58 +1,58 @@
|
|||||||
---
|
---
|
||||||
import MainSection from '@components/blocks/MainSection.astro'
|
import MainSection from '@components/blocks/MainSection.astro'
|
||||||
import BlogCard from '@components/blog/BlogCard.astro'
|
import BlogCard from '@components/blog/BlogCard.astro'
|
||||||
import BaseLayout from '@layouts/BaseLayout.astro'
|
import BaseLayout from '@layouts/BaseLayout.astro'
|
||||||
import { getCollection, type CollectionEntry } from 'astro:content'
|
import { getCollection, type CollectionEntry } from 'astro:content'
|
||||||
import { SITE } from '../../config'
|
import { SITE } from '../../config'
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const categories = await getCollection('categories')
|
const categories = await getCollection('categories')
|
||||||
return categories.map(category => ({
|
return categories.map(category => ({
|
||||||
params: { slug: category.slug },
|
params: { slug: category.slug },
|
||||||
props: { category },
|
props: { category },
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
const { category } = Astro.props
|
const { category } = Astro.props
|
||||||
const URL = Astro.url.href
|
const URL = Astro.url.href
|
||||||
const categoriesURL = `${Astro.url.origin}/categories`
|
const categoriesURL = `${Astro.url.origin}/categories`
|
||||||
|
|
||||||
const posts: CollectionEntry<'posts'>[] = (await getCollection('posts')).sort(
|
const posts: CollectionEntry<'posts'>[] = (await getCollection('posts')).sort(
|
||||||
(a: CollectionEntry<'posts'>, b: CollectionEntry<'posts'>) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
(a: CollectionEntry<'posts'>, b: CollectionEntry<'posts'>) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||||
).filter((b) => {
|
).filter((b) => {
|
||||||
return b.data.category === category.slug
|
return b.data.category === category.slug
|
||||||
});
|
});
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout
|
<BaseLayout
|
||||||
title={category.data.title}
|
title={category.data.title}
|
||||||
description={category.data.description}
|
description={category.data.description}
|
||||||
structuredData={{
|
structuredData={{
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'WebPage',
|
'@type': 'WebPage',
|
||||||
'inLanguage': 'ru-RU',
|
'inLanguage': 'ru-RU',
|
||||||
'@id': URL,
|
'@id': URL,
|
||||||
'url': URL,
|
'url': URL,
|
||||||
'name': `${category.data.title} - ${SITE.title}`,
|
'name': `${category.data.title} - ${SITE.title}`,
|
||||||
'description': category.data.description,
|
'description': category.data.description,
|
||||||
'isPartOf': {
|
'isPartOf': {
|
||||||
'@type': 'WebSite',
|
'@type': 'WebSite',
|
||||||
'url': categoriesURL,
|
'url': categoriesURL,
|
||||||
'name': `All Categories - ${SITE.title}`,
|
'name': `All Categories - ${SITE.title}`,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MainSection
|
<MainSection
|
||||||
title={category.data.title}
|
title={category.data.title}
|
||||||
subTitle={category.data.description}
|
subTitle={category.data.description}
|
||||||
btnExists={true}
|
btnExists={true}
|
||||||
btnTitle="Вернуться назад"
|
btnTitle="Вернуться назад"
|
||||||
btnURL="/categories"
|
btnURL="/categories"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<section class="mx-auto max-w-[85rem] px-4 py-8 sm:px-6 lg:px-8 mb-10 2xl:max-w-full">
|
<section class="mx-auto max-w-[85rem] px-4 py-8 sm:px-6 lg:px-8 mb-10 2xl:max-w-full">
|
||||||
<div class="grid gap-6 grid-cols-1 lg:grid-cols-3 sm:grid-cols-2">
|
<div class="grid gap-6 grid-cols-1 lg:grid-cols-3 sm:grid-cols-2">
|
||||||
{posts.map(b => <BlogCard blog={b} />)}
|
{posts.map(b => <BlogCard blog={b} />)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|||||||
@@ -1,66 +1,66 @@
|
|||||||
---
|
---
|
||||||
import MainSection from '@components/blocks/MainSection.astro'
|
import MainSection from '@components/blocks/MainSection.astro'
|
||||||
import BlogCategory from '@components/blog/BlogCategory.astro'
|
import BlogCategory from '@components/blog/BlogCategory.astro'
|
||||||
import BaseLayout from '@layouts/BaseLayout.astro'
|
import BaseLayout from '@layouts/BaseLayout.astro'
|
||||||
import type { CollectionEntry } from 'astro:content'
|
import type { CollectionEntry } from 'astro:content'
|
||||||
import { getCollection } from 'astro:content'
|
import { getCollection } from 'astro:content'
|
||||||
import { SITE } from '../../config'
|
import { SITE } from '../../config'
|
||||||
import { timeago } from '../../support/time'
|
import { timeago } from '../../support/time'
|
||||||
|
|
||||||
const postMap: Map<string, CollectionEntry<'posts'>[]> = (await getCollection('posts')).sort(
|
const postMap: Map<string, CollectionEntry<'posts'>[]> = (await getCollection('posts')).sort(
|
||||||
(a: CollectionEntry<'posts'>, b: CollectionEntry<'posts'>) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
(a: CollectionEntry<'posts'>, b: CollectionEntry<'posts'>) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||||
).reduce((acc, obj) => {
|
).reduce((acc, obj) => {
|
||||||
let posts = acc.get(obj.data.category)
|
let posts = acc.get(obj.data.category)
|
||||||
if (!posts) {
|
if (!posts) {
|
||||||
posts = []
|
posts = []
|
||||||
}
|
}
|
||||||
posts.push(obj)
|
posts.push(obj)
|
||||||
|
|
||||||
acc.set(obj.data.category, posts)
|
acc.set(obj.data.category, posts)
|
||||||
|
|
||||||
return acc
|
return acc
|
||||||
}, new Map<string, CollectionEntry<'posts'>[]>())
|
}, new Map<string, CollectionEntry<'posts'>[]>())
|
||||||
|
|
||||||
const categories = await getCollection('categories')
|
const categories = await getCollection('categories')
|
||||||
const description = 'Каждаю новость для вам мы раскладываем по категориям'
|
const description = 'Каждаю новость для вам мы раскладываем по категориям'
|
||||||
const URL = Astro.url.href
|
const URL = Astro.url.href
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout
|
<BaseLayout
|
||||||
title="All Categories"
|
title="All Categories"
|
||||||
description={description}
|
description={description}
|
||||||
structuredData={{
|
structuredData={{
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'WebPage',
|
'@type': 'WebPage',
|
||||||
'inLanguage': 'ru-RU',
|
'inLanguage': 'ru-RU',
|
||||||
'@id': URL,
|
'@id': URL,
|
||||||
'url': URL,
|
'url': URL,
|
||||||
'name': `All Categories - ${SITE.title}`,
|
'name': `All Categories - ${SITE.title}`,
|
||||||
'description': description,
|
'description': description,
|
||||||
'isPartOf': {
|
'isPartOf': {
|
||||||
'@type': 'WebSite',
|
'@type': 'WebSite',
|
||||||
'url': SITE.url,
|
'url': SITE.url,
|
||||||
'name': SITE.title,
|
'name': SITE.title,
|
||||||
'description': SITE.description,
|
'description': SITE.description,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
>
|
>
|
||||||
<MainSection title="Категории" subTitle={description} />
|
<MainSection title="Категории" subTitle={description} />
|
||||||
|
|
||||||
<section class="mx-auto px-4 py-10 sm:px-6 lg:px-8 lg:pt-10 lg:py-14 2xl:max-w-full">
|
<section class="mx-auto px-4 py-10 sm:px-6 lg:px-8 lg:pt-10 lg:py-14 2xl:max-w-full">
|
||||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 items-center gap-6 md:gap-10">
|
<div class="grid sm:grid-cols-2 lg:grid-cols-3 items-center gap-6 md:gap-10">
|
||||||
{
|
{
|
||||||
categories.map(c => (
|
categories.map(c => (
|
||||||
<BlogCategory
|
<BlogCategory
|
||||||
slug={c.slug}
|
slug={c.slug}
|
||||||
title={c.data.title}
|
title={c.data.title}
|
||||||
description={c.data.description}
|
description={c.data.description}
|
||||||
count={postMap.get(c.slug)?.length ?? 0}
|
count={postMap.get(c.slug)?.length ?? 0}
|
||||||
publishDate={timeago(postMap.get(c.slug)?.[0]?.data?.pubDate)}
|
publishDate={timeago(postMap.get(c.slug)?.[0]?.data?.pubDate)}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import type { APIRoute } from 'astro'
|
import type { APIRoute } from 'astro'
|
||||||
import sharp from 'sharp'
|
import sharp from 'sharp'
|
||||||
import ico from 'sharp-ico'
|
import ico from 'sharp-ico'
|
||||||
|
|
||||||
const faviconSrc = path.resolve('src/images/icon.png')
|
const faviconSrc = path.resolve('src/images/icon.png')
|
||||||
|
|
||||||
export const GET: APIRoute = async () => {
|
export const GET: APIRoute = async () => {
|
||||||
// Resize the image to multiple sizes
|
// Resize the image to multiple sizes
|
||||||
const sizes = [16, 32]
|
const sizes = [16, 32]
|
||||||
|
|
||||||
const buffers = await Promise.all(
|
const buffers = await Promise.all(
|
||||||
sizes.map(async (size) => {
|
sizes.map(async (size) => {
|
||||||
return await sharp(faviconSrc)
|
return await sharp(faviconSrc)
|
||||||
.resize(size)
|
.resize(size)
|
||||||
.toFormat('png')
|
.toFormat('png')
|
||||||
.toBuffer()
|
.toBuffer()
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Convert the image to an ICO file
|
// Convert the image to an ICO file
|
||||||
const icoBuffer = ico.encode(buffers)
|
const icoBuffer = ico.encode(buffers)
|
||||||
|
|
||||||
return new Response(icoBuffer, {
|
return new Response(icoBuffer, {
|
||||||
headers: { 'Content-Type': 'image/x-icon' },
|
headers: { 'Content-Type': 'image/x-icon' },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,123 +1,123 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from '@layouts/BaseLayout.astro'
|
import BaseLayout from '@layouts/BaseLayout.astro'
|
||||||
import MainSection from '@components/blocks/MainSection.astro'
|
import MainSection from '@components/blocks/MainSection.astro'
|
||||||
import { Friends } from '../config'
|
import { Friends } from '../config'
|
||||||
|
|
||||||
const friends = Friends
|
const friends = Friends
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title="Godruoyi's fiends" description="Godruoyi and his friends">
|
<BaseLayout title="Godruoyi's fiends" description="Godruoyi and his friends">
|
||||||
<MainSection title="Hey Friends" subTitle="Email me if you want to show your link here." />
|
<MainSection title="Hey Friends" subTitle="Email me if you want to show your link here." />
|
||||||
|
|
||||||
<section class="mx-auto max-w-[85rem] px-4 py-8 sm:px-6 lg:px-8 mb-10 2xl:max-w-full">
|
<section class="mx-auto max-w-[85rem] px-4 py-8 sm:px-6 lg:px-8 mb-10 2xl:max-w-full">
|
||||||
<div class="grid gap-6 grid-cols-1 lg:grid-cols-3 sm:grid-cols-2">
|
<div class="grid gap-6 grid-cols-1 lg:grid-cols-3 sm:grid-cols-2">
|
||||||
{
|
{
|
||||||
friends.map(f => (
|
friends.map(f => (
|
||||||
<div class="flex flex-col justify-between rounded-xl p-4 md:p-6 bg-white dark:bg-neutral-900/30">
|
<div class="flex flex-col justify-between rounded-xl p-4 md:p-6 bg-white dark:bg-neutral-900/30">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center gap-x-4">
|
<div class="flex items-center gap-x-4">
|
||||||
<img
|
<img
|
||||||
class="rounded-full size-20"
|
class="rounded-full size-20"
|
||||||
src={f.avatar}
|
src={f.avatar}
|
||||||
alt="Image Description"
|
alt="Image Description"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
<h3 class="font-medium text-gray-800 dark:text-neutral-200">
|
<h3 class="font-medium text-gray-800 dark:text-neutral-200">
|
||||||
{f.name}
|
{f.name}
|
||||||
</h3>
|
</h3>
|
||||||
<p class="text-xs mt-0.5 uppercase text-gray-500 dark:text-neutral-500">
|
<p class="text-xs mt-0.5 uppercase text-gray-500 dark:text-neutral-500">
|
||||||
{f.title}
|
{f.title}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-gray-500 dark:text-neutral-500">
|
<p class="mt-3 text-gray-500 dark:text-neutral-500">
|
||||||
{f.description}
|
{f.description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-3 space-x-1">
|
<div class="mt-3 space-x-1">
|
||||||
{
|
{
|
||||||
f.social?.blog && (
|
f.social?.blog && (
|
||||||
<a
|
<a
|
||||||
class="shadow-md inline-flex justify-center items-center size-8 text-sm font-semibold rounded-lg border border-gray-300 text-gray-500 hover:text-gray-600 hover:bg-gray-100 disabled:opacity-50 disabled:pointer-events-none dark:text-neutral-400 dark:hover:text-neutral-300 dark:border-neutral-800 dark:hover:bg-neutral-700"
|
class="shadow-md inline-flex justify-center items-center size-8 text-sm font-semibold rounded-lg border border-gray-300 text-gray-500 hover:text-gray-600 hover:bg-gray-100 disabled:opacity-50 disabled:pointer-events-none dark:text-neutral-400 dark:hover:text-neutral-300 dark:border-neutral-800 dark:hover:bg-neutral-700"
|
||||||
href={f.social.blog}
|
href={f.social.blog}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
class="flex-shrink-0 size-3.5"
|
class="flex-shrink-0 size-3.5"
|
||||||
>
|
>
|
||||||
<circle cx="12" cy="12" r="10" />
|
<circle cx="12" cy="12" r="10" />
|
||||||
<circle cx="12" cy="12" r="4" />
|
<circle cx="12" cy="12" r="4" />
|
||||||
<line x1="21.17" x2="12" y1="8" y2="8" />
|
<line x1="21.17" x2="12" y1="8" y2="8" />
|
||||||
<line x1="3.95" x2="8.54" y1="6.06" y2="14" />
|
<line x1="3.95" x2="8.54" y1="6.06" y2="14" />
|
||||||
<line x1="10.88" x2="15.46" y1="21.94" y2="14" />
|
<line x1="10.88" x2="15.46" y1="21.94" y2="14" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
f.social?.twitter && (
|
f.social?.twitter && (
|
||||||
<a
|
<a
|
||||||
class="shadow-md inline-flex justify-center items-center size-8 text-sm font-semibold rounded-lg border border-gray-300 text-gray-500 hover:text-gray-600 hover:bg-gray-100 disabled:opacity-50 disabled:pointer-events-none dark:text-neutral-400 dark:hover:text-neutral-300 dark:border-neutral-800 dark:hover:bg-neutral-700"
|
class="shadow-md inline-flex justify-center items-center size-8 text-sm font-semibold rounded-lg border border-gray-300 text-gray-500 hover:text-gray-600 hover:bg-gray-100 disabled:opacity-50 disabled:pointer-events-none dark:text-neutral-400 dark:hover:text-neutral-300 dark:border-neutral-800 dark:hover:bg-neutral-700"
|
||||||
href=`https://twitter.com/${f.social.twitter}`
|
href=`https://twitter.com/${f.social.twitter}`
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
class="flex-shrink-0 size-3.5"
|
class="flex-shrink-0 size-3.5"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6 2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4-.9-4.2 4-6.6 7-3.8 1.1 0 3-1.2 3-1.2z"
|
d="M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6 2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4-.9-4.2 4-6.6 7-3.8 1.1 0 3-1.2 3-1.2z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
f.social?.github && (
|
f.social?.github && (
|
||||||
<a
|
<a
|
||||||
class="shadow-md inline-flex justify-center items-center size-8 text-sm font-semibold rounded-lg border border-gray-300 text-gray-500 hover:text-gray-600 hover:bg-gray-100 disabled:opacity-50 disabled:pointer-events-none dark:text-neutral-400 dark:hover:text-neutral-300 dark:border-neutral-800 dark:hover:bg-neutral-700"
|
class="shadow-md inline-flex justify-center items-center size-8 text-sm font-semibold rounded-lg border border-gray-300 text-gray-500 hover:text-gray-600 hover:bg-gray-100 disabled:opacity-50 disabled:pointer-events-none dark:text-neutral-400 dark:hover:text-neutral-300 dark:border-neutral-800 dark:hover:bg-neutral-700"
|
||||||
href=`https://github.com/${f.social.github}`
|
href=`https://github.com/${f.social.github}`
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="flex-shrink-0 size-3.5"
|
class="flex-shrink-0 size-3.5"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"
|
d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"
|
||||||
/>
|
/>
|
||||||
<path d="M9 18c-4.51 2-5-2-7-2" />
|
<path d="M9 18c-4.51 2-5-2-7-2" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
---
|
---
|
||||||
import FeaturesSection from '@components/blocks/FeaturesSection.astro'
|
import FeaturesSection from '@components/blocks/FeaturesSection.astro'
|
||||||
import HeroSection from '@components/blocks/HeroSection.astro'
|
import HeroSection from '@components/blocks/HeroSection.astro'
|
||||||
import HeroSectionAlt from '@components/blocks/HeroSectionAlt.astro'
|
import HeroSectionAlt from '@components/blocks/HeroSectionAlt.astro'
|
||||||
import BlogInsight from '@components/blog/BlogInsight.astro'
|
import BlogInsight from '@components/blog/BlogInsight.astro'
|
||||||
import BaseLayout from '@layouts/BaseLayout.astro'
|
import BaseLayout from '@layouts/BaseLayout.astro'
|
||||||
import type { CollectionEntry } from 'astro:content'
|
import type { CollectionEntry } from 'astro:content'
|
||||||
import { getCollection } from 'astro:content'
|
import { getCollection } from 'astro:content'
|
||||||
|
|
||||||
const posts: CollectionEntry<'posts'>[] = (await getCollection('posts')).sort(
|
const posts: CollectionEntry<'posts'>[] = (await getCollection('posts')).sort(
|
||||||
(a: CollectionEntry<'posts'>, b: CollectionEntry<'posts'>) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
(a: CollectionEntry<'posts'>, b: CollectionEntry<'posts'>) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||||
).slice(0, 3);
|
).slice(0, 3);
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<HeroSection
|
<HeroSection
|
||||||
title=`Уважаемые господа!`
|
title=`Уважаемые господа!`
|
||||||
subTitle="Настоящим сообщаем, что наша компания готова оказать профессиональные услуги по разработке и внедрению системы ХАССП (Hazard Analysis and Critical Control Points"
|
subTitle="Настоящим сообщаем, что наша компания готова оказать профессиональные услуги по разработке и внедрению системы ХАССП (Hazard Analysis and Critical Control Points"
|
||||||
primaryBtn="Explore Posts"
|
primaryBtn="Explore Posts"
|
||||||
primaryBtnURL="/posts"
|
primaryBtnURL="/posts"
|
||||||
src={}
|
src={}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FeaturesSection />
|
<FeaturesSection />
|
||||||
|
|
||||||
<section class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full">
|
<section class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full">
|
||||||
<div class="mx-auto mb-10 max-w-2xl text-center lg:mb-14">
|
<div class="mx-auto mb-10 max-w-2xl text-center lg:mb-14">
|
||||||
<h2 class="text-3xl font-bold text-neutral-800 dark:text-neutral-200 md:text-4xl md:leading-tight">
|
<h2 class="text-3xl font-bold text-neutral-800 dark:text-neutral-200 md:text-4xl md:leading-tight">
|
||||||
Наши работы
|
Наши работы
|
||||||
</h2>
|
</h2>
|
||||||
<p class="mt-1 text-pretty text-neutral-600 dark:text-neutral-400">
|
<p class="mt-1 text-pretty text-neutral-600 dark:text-neutral-400">
|
||||||
Уже более 1000 реализованных проектов в регионе
|
Уже более 1000 реализованных проектов в регионе
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
{
|
{
|
||||||
posts.map(b => (
|
posts.map(b => (
|
||||||
<BlogInsight blog={b} />
|
<BlogInsight blog={b} />
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<HeroSectionAlt
|
<HeroSectionAlt
|
||||||
title="Для связи с нами"
|
title="Для связи с нами"
|
||||||
subTitle="Вы можете выбрать любой способ, но проще всегда просто позвонить 8(924)535-65-66"
|
subTitle="Вы можете выбрать любой способ, но проще всегда просто позвонить 8(924)535-65-66"
|
||||||
url="tel://+79245356566"
|
url="tel://+79245356566"
|
||||||
/>
|
/>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|||||||
@@ -1,137 +1,137 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from '@layouts/BaseLayout.astro'
|
import BaseLayout from '@layouts/BaseLayout.astro'
|
||||||
import { Image } from 'astro:assets'
|
import { Image } from 'astro:assets'
|
||||||
import { getCollection, type CollectionEntry } from 'astro:content'
|
import { getCollection, type CollectionEntry } from 'astro:content'
|
||||||
import { SITE } from '../../config'
|
import { SITE } from '../../config'
|
||||||
import { timeago } from '../../support/time'
|
import { timeago } from '../../support/time'
|
||||||
|
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const posts = await getCollection('posts')
|
const posts = await getCollection('posts')
|
||||||
return posts.map(post => ({
|
return posts.map(post => ({
|
||||||
params: { slug: post.slug },
|
params: { slug: post.slug },
|
||||||
props: post,
|
props: post,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
type Props = CollectionEntry<'posts'>
|
type Props = CollectionEntry<'posts'>
|
||||||
|
|
||||||
const post = Astro.props
|
const post = Astro.props
|
||||||
const { Content, remarkPluginFrontmatter } = await post.render()
|
const { Content, remarkPluginFrontmatter } = await post.render()
|
||||||
|
|
||||||
const category: CollectionEntry<'categories'> = (await getCollection('categories')).filter(
|
const category: CollectionEntry<'categories'> = (await getCollection('categories')).filter(
|
||||||
c => c.slug === post.data.category,
|
c => c.slug === post.data.category,
|
||||||
).pop()
|
).pop()
|
||||||
|
|
||||||
const tags = post.data.tags ?? [category.slug]
|
const tags = post.data.tags ?? [category.slug]
|
||||||
const URL = Astro.url.href
|
const URL = Astro.url.href
|
||||||
const ogImage = post.data.ogImage
|
const ogImage = post.data.ogImage
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout
|
<BaseLayout
|
||||||
title={post.data.title}
|
title={post.data.title}
|
||||||
description={post.data.description}
|
description={post.data.description}
|
||||||
ogImage={ogImage}
|
ogImage={ogImage}
|
||||||
structuredData={{
|
structuredData={{
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'NewsArticle',
|
'@type': 'NewsArticle',
|
||||||
'@id': URL,
|
'@id': URL,
|
||||||
'url': URL,
|
'url': URL,
|
||||||
'description': post.data.description,
|
'description': post.data.description,
|
||||||
'image': [
|
'image': [
|
||||||
// post.data.banner,
|
// post.data.banner,
|
||||||
],
|
],
|
||||||
'headline': post.data.title,
|
'headline': post.data.title,
|
||||||
'datePublished': post.data.pubDate,
|
'datePublished': post.data.pubDate,
|
||||||
'dateModified': post.data.pubDate,
|
'dateModified': post.data.pubDate,
|
||||||
'author': [{
|
'author': [{
|
||||||
'@type': 'Person',
|
'@type': 'Person',
|
||||||
'name': SITE.author,
|
'name': SITE.author,
|
||||||
'url': SITE.url,
|
'url': SITE.url,
|
||||||
}],
|
}],
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<section class="mx-auto max-w-6xl px-4 pb-12 pt-8 sm:px-6 lg:px-8 lg:pt-12">
|
<section class="mx-auto max-w-6xl px-4 pb-12 pt-8 sm:px-6 lg:px-8 lg:pt-12">
|
||||||
<div class="shadow-none sm:shadow-sm mt-4 sm:mt-0">
|
<div class="shadow-none sm:shadow-sm mt-4 sm:mt-0">
|
||||||
<div class="max-w-6xl">
|
<div class="max-w-6xl">
|
||||||
<Image
|
<Image
|
||||||
class="w-full object-cover rounded-tl-sm rounded-tr-sm"
|
class="w-full object-cover rounded-tl-sm rounded-tr-sm"
|
||||||
src={post.data.banner}
|
src={post.data.banner}
|
||||||
alt={post.data.title}
|
alt={post.data.title}
|
||||||
draggable="false"
|
draggable="false"
|
||||||
format="avif"
|
format="avif"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="sm:dark:bg-neutral-900/30 sm:bg-neutral-100 px-0 sm:px-6 md:px-10 lg:px-14 py-6">
|
<div class="sm:dark:bg-neutral-900/30 sm:bg-neutral-100 px-0 sm:px-6 md:px-10 lg:px-14 py-6">
|
||||||
<div class="mb-10">
|
<div class="mb-10">
|
||||||
<h2 class="block text-balance text-3xl font-bold tracking-tight text-neutral-800 dark:text-neutral-300 md:text-4xl lg:text-5xl">
|
<h2 class="block text-balance text-3xl font-bold tracking-tight text-neutral-800 dark:text-neutral-300 md:text-4xl lg:text-5xl">
|
||||||
{post.data.title}
|
{post.data.title}
|
||||||
</h2>
|
</h2>
|
||||||
<ol class="flex items-center whitespace-nowrap mt-2">
|
<ol class="flex items-center whitespace-nowrap mt-2">
|
||||||
<li class="inline-flex items-center">
|
<li class="inline-flex items-center">
|
||||||
<a
|
<a
|
||||||
class="flex items-center text-sm text-orange-400 hover:text-orange-500 focus:outline-none focus:text-orange-500"
|
class="flex items-center text-sm text-orange-400 hover:text-orange-500 focus:outline-none focus:text-orange-500"
|
||||||
href=`/categories/${category.slug}`
|
href=`/categories/${category.slug}`
|
||||||
>
|
>
|
||||||
{category?.data?.title}
|
{category?.data?.title}
|
||||||
</a>
|
</a>
|
||||||
<svg
|
<svg
|
||||||
class="flex-shrink-0 size-5 text-neutral-500 dark:text-neutral-600 mx-2"
|
class="flex-shrink-0 size-5 text-neutral-500 dark:text-neutral-600 mx-2"
|
||||||
width="16"
|
width="16"
|
||||||
height="16"
|
height="16"
|
||||||
viewBox="0 0 16 16"
|
viewBox="0 0 16 16"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
<path d="M6 13L10 3" stroke="currentColor" stroke-linecap="round"></path>
|
<path d="M6 13L10 3" stroke="currentColor" stroke-linecap="round"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</li>
|
</li>
|
||||||
<li class="inline-flex items-center text-sm text-neutral-500 hover:text-neutral-600 focus:outline-none focus:text-neutral-600 dark:text-neutral-500 dark:hover:text-neutral-400 dark:focus:text-neutral-400">
|
<li class="inline-flex items-center text-sm text-neutral-500 hover:text-neutral-600 focus:outline-none focus:text-neutral-600 dark:text-neutral-500 dark:hover:text-neutral-400 dark:focus:text-neutral-400">
|
||||||
{timeago(post.data.pubDate)}
|
{timeago(post.data.pubDate)}
|
||||||
<svg
|
<svg
|
||||||
class="flex-shrink-0 size-5 text-neutral-500 dark:text-neutral-600 mx-2"
|
class="flex-shrink-0 size-5 text-neutral-500 dark:text-neutral-600 mx-2"
|
||||||
width="16"
|
width="16"
|
||||||
height="16"
|
height="16"
|
||||||
viewBox="0 0 16 16"
|
viewBox="0 0 16 16"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
<path d="M6 13L10 3" stroke="currentColor" stroke-linecap="round"></path>
|
<path d="M6 13L10 3" stroke="currentColor" stroke-linecap="round"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="inline-flex items-center text-sm text-neutral-500 hover:text-neutral-600 focus:outline-none focus:text-neutral-600 dark:text-neutral-500 dark:hover:text-neutral-400 dark:focus:text-neutral-400"
|
class="inline-flex items-center text-sm text-neutral-500 hover:text-neutral-600 focus:outline-none focus:text-neutral-600 dark:text-neutral-500 dark:hover:text-neutral-400 dark:focus:text-neutral-400"
|
||||||
aria-current="page"
|
aria-current="page"
|
||||||
>
|
>
|
||||||
{remarkPluginFrontmatter.minutesRead}
|
{remarkPluginFrontmatter.minutesRead}
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<article class="prose prose-blog sm:prose-lg dark:prose-invert max-w-none">
|
<article class="prose prose-blog sm:prose-lg dark:prose-invert max-w-none">
|
||||||
<Content />
|
<Content />
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<div class="mt-10 md:mt-14 mx-auto grid max-w-screen-lg gap-y-5 sm:flex sm:items-center sm:justify-between sm:gap-y-0">
|
<div class="mt-10 md:mt-14 mx-auto grid max-w-screen-lg gap-y-5 sm:flex sm:items-center sm:justify-between sm:gap-y-0">
|
||||||
<div class="flex flex-wrap gap-x-2 gap-y-1 sm:flex-nowrap sm:items-center sm:gap-y-0">
|
<div class="flex flex-wrap gap-x-2 gap-y-1 sm:flex-nowrap sm:items-center sm:gap-y-0">
|
||||||
{
|
{
|
||||||
tags.map((tag: string) => (
|
tags.map((tag: string) => (
|
||||||
<span class="inline-flex items-center gap-x-1.5 rounded-lg bg-neutral-400/30 px-3 py-1.5 text-xs font-medium text-neutral-700 outline-none focus:outline-none focus-visible:outline-none focus-visible:ring dark:bg-neutral-700/60 dark:text-neutral-300">
|
<span class="inline-flex items-center gap-x-1.5 rounded-lg bg-neutral-400/30 px-3 py-1.5 text-xs font-medium text-neutral-700 outline-none focus:outline-none focus-visible:outline-none focus-visible:ring dark:bg-neutral-700/60 dark:text-neutral-300">
|
||||||
{tag}
|
{tag}
|
||||||
</span>
|
</span>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<!--<div class="flex items-center justify-end gap-x-1.5">-->
|
<!--<div class="flex items-center justify-end gap-x-1.5">-->
|
||||||
<!-- <Bookmark />-->
|
<!-- <Bookmark />-->
|
||||||
<!-- <div class="mx-3 block h-4 border-e border-neutral-400 dark:border-neutral-500"></div>-->
|
<!-- <div class="mx-3 block h-4 border-e border-neutral-400 dark:border-neutral-500"></div>-->
|
||||||
<!-- <div class="inline-flex">-->
|
<!-- <div class="inline-flex">-->
|
||||||
<!-- <SocialShare pageTitle={post.data.title} />-->
|
<!-- <SocialShare pageTitle={post.data.title} />-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!--</div>-->
|
<!--</div>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|||||||
@@ -1,49 +1,49 @@
|
|||||||
---
|
---
|
||||||
import MainSection from '@components/blocks/MainSection.astro'
|
import MainSection from '@components/blocks/MainSection.astro'
|
||||||
import BlogRecentCard from '@components/blog/BlogRecentCard.astro'
|
import BlogRecentCard from '@components/blog/BlogRecentCard.astro'
|
||||||
import BlogSelectedArticle from '@components/blog/BlogSelectedArticle.astro'
|
import BlogSelectedArticle from '@components/blog/BlogSelectedArticle.astro'
|
||||||
import BaseLayout from '@layouts/BaseLayout.astro'
|
import BaseLayout from '@layouts/BaseLayout.astro'
|
||||||
import type { CollectionEntry } from 'astro:content'
|
import type { CollectionEntry } from 'astro:content'
|
||||||
import { getCollection } from 'astro:content'
|
import { getCollection } from 'astro:content'
|
||||||
import { SITE } from '../../config'
|
import { SITE } from '../../config'
|
||||||
|
|
||||||
const posts: CollectionEntry<'posts'>[] = (await getCollection('posts')).sort(
|
const posts: CollectionEntry<'posts'>[] = (await getCollection('posts')).sort(
|
||||||
(a: CollectionEntry<'posts'>, b: CollectionEntry<'posts'>) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
(a: CollectionEntry<'posts'>, b: CollectionEntry<'posts'>) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||||
)
|
)
|
||||||
|
|
||||||
const selectedPosts: CollectionEntry<'posts'>[] = posts.filter(p => p.data.selected)
|
const selectedPosts: CollectionEntry<'posts'>[] = posts.filter(p => p.data.selected)
|
||||||
const description = 'Here are some articles that Godruoyi believes are not bad, hope you enjoy them.'
|
const description = 'Here are some articles that Godruoyi believes are not bad, hope you enjoy them.'
|
||||||
const URL = Astro.url.href
|
const URL = Astro.url.href
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout
|
<BaseLayout
|
||||||
title="All Blogs"
|
title="All Blogs"
|
||||||
description={description}
|
description={description}
|
||||||
structuredData={{
|
structuredData={{
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'WebPage',
|
'@type': 'WebPage',
|
||||||
'inLanguage': 'ru-RU',
|
'inLanguage': 'ru-RU',
|
||||||
'@id': URL,
|
'@id': URL,
|
||||||
'url': URL,
|
'url': URL,
|
||||||
'name': `All Blogs - ${SITE.title}`,
|
'name': `All Blogs - ${SITE.title}`,
|
||||||
'description': description,
|
'description': description,
|
||||||
'isPartOf': {
|
'isPartOf': {
|
||||||
'@type': 'WebSite',
|
'@type': 'WebSite',
|
||||||
'url': SITE.url,
|
'url': SITE.url,
|
||||||
'name': SITE.title,
|
'name': SITE.title,
|
||||||
'description': SITE.description,
|
'description': SITE.description,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MainSection
|
<MainSection
|
||||||
title="Раздел статей"
|
title="Раздел статей"
|
||||||
subTitle="Это главный радел где мы попликуем все жизнь нашей комапнии"
|
subTitle="Это главный радел где мы попликуем все жизнь нашей комапнии"
|
||||||
btnExists={true}
|
btnExists={true}
|
||||||
btnTitle="Все стати"
|
btnTitle="Все стати"
|
||||||
btnURL="/timeline"
|
btnURL="/timeline"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<BlogRecentCard blogs={posts} />
|
<BlogRecentCard blogs={posts} />
|
||||||
<BlogSelectedArticle posts={selectedPosts} />
|
<BlogSelectedArticle posts={selectedPosts} />
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
// https://docs.astro.build/en/guides/integrations-guide/sitemap/#usage
|
// https://docs.astro.build/en/guides/integrations-guide/sitemap/#usage
|
||||||
import type { APIRoute } from 'astro'
|
import type { APIRoute } from 'astro'
|
||||||
|
|
||||||
const robotsTxt = `
|
const robotsTxt = `
|
||||||
User-agent: Googlebot
|
User-agent: Googlebot
|
||||||
Disallow:
|
Disallow:
|
||||||
Allow: /
|
Allow: /
|
||||||
Crawl-delay: 10
|
Crawl-delay: 10
|
||||||
|
|
||||||
User-agent: Yandex
|
User-agent: Yandex
|
||||||
Disallow:
|
Disallow:
|
||||||
Allow: /
|
Allow: /
|
||||||
Crawl-delay: 2
|
Crawl-delay: 2
|
||||||
|
|
||||||
User-agent: archive.org_bot
|
User-agent: archive.org_bot
|
||||||
Disallow:
|
Disallow:
|
||||||
Allow: /
|
Allow: /
|
||||||
Crawl-delay: 2
|
Crawl-delay: 2
|
||||||
|
|
||||||
User-agent: *
|
User-agent: *
|
||||||
Allow: /
|
Allow: /
|
||||||
|
|
||||||
Sitemap: ${new URL('sitemap-index.xml', import.meta.env.SITE).href}`.trim()
|
Sitemap: ${new URL('sitemap-index.xml', import.meta.env.SITE).href}`.trim()
|
||||||
|
|
||||||
export const GET: APIRoute = () => {
|
export const GET: APIRoute = () => {
|
||||||
return new Response(robotsTxt, {
|
return new Response(robotsTxt, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/plain; charset=utf-8',
|
'Content-Type': 'text/plain; charset=utf-8',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +1,51 @@
|
|||||||
import rss, { type RSSFeedItem } from '@astrojs/rss'
|
import rss, { type RSSFeedItem } from '@astrojs/rss'
|
||||||
import { getCollection } from 'astro:content'
|
import { getCollection } from 'astro:content'
|
||||||
import type { CollectionEntry } from 'astro:content'
|
import type { CollectionEntry } from 'astro:content'
|
||||||
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
|
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
|
||||||
import RSSRender from '@components/support/RSSRender.astro'
|
import RSSRender from '@components/support/RSSRender.astro'
|
||||||
import { transform, walk } from 'ultrahtml'
|
import { transform, walk } from 'ultrahtml'
|
||||||
import sanitize from 'ultrahtml/transformers/sanitize'
|
import sanitize from 'ultrahtml/transformers/sanitize'
|
||||||
import { SITE } from '../config.ts'
|
import { SITE } from '../config.ts'
|
||||||
|
|
||||||
// see https://github.com/delucis/astro-blog-full-text-rss/blob/latest/src/pages/rss.xml.ts
|
// see https://github.com/delucis/astro-blog-full-text-rss/blob/latest/src/pages/rss.xml.ts
|
||||||
// get more context
|
// get more context
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const container = await AstroContainer.create({
|
const container = await AstroContainer.create({
|
||||||
renderers: [
|
renderers: [
|
||||||
{ name: '@astrojs/mdx', serverEntrypoint: 'astro/jsx/server.js' },
|
{ name: '@astrojs/mdx', serverEntrypoint: 'astro/jsx/server.js' },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
const posts: CollectionEntry<'posts'>[] = (await getCollection('posts')).sort(
|
const posts: CollectionEntry<'posts'>[] = (await getCollection('posts')).sort(
|
||||||
(a: CollectionEntry<'posts'>, b: CollectionEntry<'posts'>) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
(a: CollectionEntry<'posts'>, b: CollectionEntry<'posts'>) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||||
)
|
)
|
||||||
|
|
||||||
const feedItems: RSSFeedItem[] = []
|
const feedItems: RSSFeedItem[] = []
|
||||||
for (const { data, slug, collection } of posts) {
|
for (const { data, slug, collection } of posts) {
|
||||||
const rawContent = await container.renderToString(RSSRender, {
|
const rawContent = await container.renderToString(RSSRender, {
|
||||||
params: { collection, slug },
|
params: { collection, slug },
|
||||||
})
|
})
|
||||||
const content = await transform(rawContent, [
|
const content = await transform(rawContent, [
|
||||||
async (node) => {
|
async (node) => {
|
||||||
await walk(node, (node) => {
|
await walk(node, (node) => {
|
||||||
if (node.name === 'a' && node.attributes.href?.startsWith('/')) {
|
if (node.name === 'a' && node.attributes.href?.startsWith('/')) {
|
||||||
node.attributes.href = SITE.url + node.attributes.href
|
node.attributes.href = SITE.url + node.attributes.href
|
||||||
}
|
}
|
||||||
if (node.name === 'img' && node.attributes.src?.startsWith('/')) {
|
if (node.name === 'img' && node.attributes.src?.startsWith('/')) {
|
||||||
node.attributes.src = SITE.url + node.attributes.src
|
node.attributes.src = SITE.url + node.attributes.src
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return node
|
return node
|
||||||
},
|
},
|
||||||
sanitize({ dropElements: ['script', 'style'] }),
|
sanitize({ dropElements: ['script', 'style'] }),
|
||||||
])
|
])
|
||||||
feedItems.push({ ...data, link: `/posts/${slug}/`, content })
|
feedItems.push({ ...data, link: `/posts/${slug}/`, content })
|
||||||
}
|
}
|
||||||
|
|
||||||
return rss({
|
return rss({
|
||||||
title: SITE.title,
|
title: SITE.title,
|
||||||
description: SITE.description,
|
description: SITE.description,
|
||||||
site: SITE.url,
|
site: SITE.url,
|
||||||
items: feedItems,
|
items: feedItems,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,43 @@
|
|||||||
---
|
---
|
||||||
import BlogTimeline from '@components/blog/BlogTimeline.astro'
|
import BlogTimeline from '@components/blog/BlogTimeline.astro'
|
||||||
import BaseLayout from '@layouts/BaseLayout.astro'
|
import BaseLayout from '@layouts/BaseLayout.astro'
|
||||||
import type { CollectionEntry } from 'astro:content'
|
import type { CollectionEntry } from 'astro:content'
|
||||||
import { getCollection } from 'astro:content'
|
import { getCollection } from 'astro:content'
|
||||||
import { SITE } from '../config'
|
import { SITE } from '../config'
|
||||||
|
|
||||||
const posts: CollectionEntry<'posts'>[] = (await getCollection('posts')).sort(
|
const posts: CollectionEntry<'posts'>[] = (await getCollection('posts')).sort(
|
||||||
(a: CollectionEntry<'posts'>, b: CollectionEntry<'posts'>) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
(a: CollectionEntry<'posts'>, b: CollectionEntry<'posts'>) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||||
)
|
)
|
||||||
const description = `Godruoyi All Posts, Currently written ${posts.length} articles. Let's keep up the good work!`
|
const description = `Godruoyi All Posts, Currently written ${posts.length} articles. Let's keep up the good work!`
|
||||||
const URL = Astro.url.href
|
const URL = Astro.url.href
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout
|
<BaseLayout
|
||||||
title="Timeline"
|
title="Timeline"
|
||||||
description={description}
|
description={description}
|
||||||
structuredData={{
|
structuredData={{
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'WebPage',
|
'@type': 'WebPage',
|
||||||
'inLanguage': 'ru-RU',
|
'inLanguage': 'ru-RU',
|
||||||
'@id': URL,
|
'@id': URL,
|
||||||
'url': URL,
|
'url': URL,
|
||||||
'name': `Timeline - ${SITE.title}`,
|
'name': `Timeline - ${SITE.title}`,
|
||||||
'description': description,
|
'description': description,
|
||||||
'isPartOf': {
|
'isPartOf': {
|
||||||
'@type': 'WebSite',
|
'@type': 'WebSite',
|
||||||
'url': SITE.url,
|
'url': SITE.url,
|
||||||
'name': SITE.title,
|
'name': SITE.title,
|
||||||
'description': SITE.description,
|
'description': SITE.description,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<section class="mx-auto max-w-[85rem] mt-10 lg:mt-10 px-4 py-10 sm:px-6 lg:px-8 lg:py-14">
|
<section class="mx-auto max-w-[85rem] mt-10 lg:mt-10 px-4 py-10 sm:px-6 lg:px-8 lg:py-14">
|
||||||
<div class="max-w-3xl mx-auto mb-10 lg:mb-14">
|
<div class="max-w-3xl mx-auto mb-10 lg:mb-14">
|
||||||
<h2 class="text-3xl font-bold text-neutral-800 dark:text-neutral-200 md:text-4xl md:leading-tight">Timeline</h2>
|
<h2 class="text-3xl font-bold text-neutral-800 dark:text-neutral-200 md:text-4xl md:leading-tight">Timeline</h2>
|
||||||
<p class="mt-4 text-lg group text-pretty text-neutral-600 dark:text-neutral-400">
|
<p class="mt-4 text-lg group text-pretty text-neutral-600 dark:text-neutral-400">
|
||||||
目前一共写了 <span class="group-hover:text-yellow-500 group-hover:dark:text-yellow-400">{posts.length}</span> 篇文章,再接再厉吧 💪
|
目前一共写了 <span class="group-hover:text-yellow-500 group-hover:dark:text-yellow-400">{posts.length}</span> 篇文章,再接再厉吧 💪
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{posts.map(p => <BlogTimeline blog={p} />)}
|
{posts.map(p => <BlogTimeline blog={p} />)}
|
||||||
</section>
|
</section>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|||||||
@@ -1,67 +1,67 @@
|
|||||||
import { format, register } from 'timeago.js'
|
import { format, register } from 'timeago.js'
|
||||||
import getReadingTime from 'reading-time'
|
import getReadingTime from 'reading-time'
|
||||||
import { toString } from 'mdast-util-to-string'
|
import { toString } from 'mdast-util-to-string'
|
||||||
|
|
||||||
const TimeAgoConfiguration: string[][] = [
|
const TimeAgoConfiguration: string[][] = [
|
||||||
['today', 'today'],
|
['today', 'today'],
|
||||||
['%s seconds ago', 'in %s seconds'],
|
['%s seconds ago', 'in %s seconds'],
|
||||||
['1 minute ago', 'in 1 minute'],
|
['1 minute ago', 'in 1 minute'],
|
||||||
['%s minutes ago', 'in %s minutes'],
|
['%s minutes ago', 'in %s minutes'],
|
||||||
['1 hour ago', 'in 1 hour'],
|
['1 hour ago', 'in 1 hour'],
|
||||||
['%s hours ago', 'in %s hours'],
|
['%s hours ago', 'in %s hours'],
|
||||||
['1 day ago', 'in 1 day'],
|
['1 day ago', 'in 1 day'],
|
||||||
['%s days ago', 'in %s days'],
|
['%s days ago', 'in %s days'],
|
||||||
['1 week ago', 'in 1 week'],
|
['1 week ago', 'in 1 week'],
|
||||||
['%s weeks ago', 'in %s weeks'],
|
['%s weeks ago', 'in %s weeks'],
|
||||||
['1 month ago', 'in 1 month'],
|
['1 month ago', 'in 1 month'],
|
||||||
['%s months ago', 'in %s months'],
|
['%s months ago', 'in %s months'],
|
||||||
['1 year ago', 'in 1 year'],
|
['1 year ago', 'in 1 year'],
|
||||||
['%s years ago', 'in %s years'],
|
['%s years ago', 'in %s years'],
|
||||||
]
|
]
|
||||||
|
|
||||||
function formatDate(date: Date): string {
|
function formatDate(date: Date): string {
|
||||||
const year = date.getFullYear()
|
const year = date.getFullYear()
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
const day = String(date.getDate()).padStart(2, '0')
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
|
|
||||||
return `${year}/${month}/${day}`
|
return `${year}/${month}/${day}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDateFull(date: Date): string {
|
function formatDateFull(date: Date): string {
|
||||||
const yyyy = date.getFullYear()
|
const yyyy = date.getFullYear()
|
||||||
const mm = String(date.getMonth() + 1).padStart(2, '0')
|
const mm = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
const dd = String(date.getDate()).padStart(2, '0')
|
const dd = String(date.getDate()).padStart(2, '0')
|
||||||
|
|
||||||
const hh = String(date.getHours()).padStart(2, '0')
|
const hh = String(date.getHours()).padStart(2, '0')
|
||||||
const mi = String(date.getMinutes()).padStart(2, '0')
|
const mi = String(date.getMinutes()).padStart(2, '0')
|
||||||
const ss = String(date.getSeconds()).padStart(2, '0')
|
const ss = String(date.getSeconds()).padStart(2, '0')
|
||||||
|
|
||||||
return `${yyyy}-${mm}-${dd} ${hh}:${mi}:${ss}`
|
return `${yyyy}-${mm}-${dd} ${hh}:${mi}:${ss}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function timeago(date?: Date): string {
|
function timeago(date?: Date): string {
|
||||||
if (!date) {
|
if (!date) {
|
||||||
return 'today'
|
return 'today'
|
||||||
}
|
}
|
||||||
|
|
||||||
const localeFunc = (number: number, index: number, _?: number): [string, string] => {
|
const localeFunc = (number: number, index: number, _?: number): [string, string] => {
|
||||||
return TimeAgoConfiguration[index] as [string, string]
|
return TimeAgoConfiguration[index] as [string, string]
|
||||||
}
|
}
|
||||||
|
|
||||||
register('timeago', localeFunc)
|
register('timeago', localeFunc)
|
||||||
|
|
||||||
return format(date, 'timeago')
|
return format(date, 'timeago')
|
||||||
}
|
}
|
||||||
|
|
||||||
function remarkReadingTime() {
|
function remarkReadingTime() {
|
||||||
// eslint-disable-next-line ts/ban-ts-comment
|
// eslint-disable-next-line ts/ban-ts-comment
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
return function (tree, { data }) {
|
return function (tree, { data }) {
|
||||||
const textOnPage = toString(tree)
|
const textOnPage = toString(tree)
|
||||||
const readingTime = getReadingTime(textOnPage)
|
const readingTime = getReadingTime(textOnPage)
|
||||||
|
|
||||||
data.astro.frontmatter.minutesRead = readingTime.text
|
data.astro.frontmatter.minutesRead = readingTime.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { formatDate, timeago, formatDateFull, remarkReadingTime }
|
export { formatDate, timeago, formatDateFull, remarkReadingTime }
|
||||||
|
|||||||
62
src/types.ts
62
src/types.ts
@@ -1,31 +1,31 @@
|
|||||||
export interface Site {
|
export interface Site {
|
||||||
title: string
|
title: string
|
||||||
author: string
|
author: string
|
||||||
url: string
|
url: string
|
||||||
description: string
|
description: string
|
||||||
shortDescription: string
|
shortDescription: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NavigationLink {
|
export interface NavigationLink {
|
||||||
name: string
|
name: string
|
||||||
url: string
|
url: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PickUpPost {
|
export interface PickUpPost {
|
||||||
title: string
|
title: string
|
||||||
slug: string
|
slug: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Social {
|
export interface Social {
|
||||||
twitter?: string
|
twitter?: string
|
||||||
blog?: string
|
blog?: string
|
||||||
github?: string
|
github?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
avatar: string
|
avatar: string
|
||||||
name: string
|
name: string
|
||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
social: Social
|
social: Social
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,122 +1,122 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
content: [
|
content: [
|
||||||
'./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
|
'./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
|
||||||
'./node_modules/preline/preline.js',
|
'./node_modules/preline/preline.js',
|
||||||
],
|
],
|
||||||
darkMode: 'class',
|
darkMode: 'class',
|
||||||
theme: {
|
theme: {
|
||||||
colors: {
|
colors: {
|
||||||
transparent: 'transparent',
|
transparent: 'transparent',
|
||||||
current: 'currentColor',
|
current: 'currentColor',
|
||||||
black: '#000000',
|
black: '#000000',
|
||||||
white: '#ffffff',
|
white: '#ffffff',
|
||||||
gray: {
|
gray: {
|
||||||
100: '#f3f4f6',
|
100: '#f3f4f6',
|
||||||
300: '#d1d5db',
|
300: '#d1d5db',
|
||||||
500: '#6b7280',
|
500: '#6b7280',
|
||||||
600: '#4b5563',
|
600: '#4b5563',
|
||||||
800: '#1f2937',
|
800: '#1f2937',
|
||||||
},
|
},
|
||||||
indigo: {
|
indigo: {
|
||||||
200: '#c7d2fe',
|
200: '#c7d2fe',
|
||||||
300: '#a5b4fc',
|
300: '#a5b4fc',
|
||||||
},
|
},
|
||||||
neutral: {
|
neutral: {
|
||||||
50: '#fafafa',
|
50: '#fafafa',
|
||||||
100: '#f5f5f5',
|
100: '#f5f5f5',
|
||||||
200: '#e5e5e5',
|
200: '#e5e5e5',
|
||||||
300: '#d4d4d4',
|
300: '#d4d4d4',
|
||||||
350: '#bfbfbf',
|
350: '#bfbfbf',
|
||||||
400: '#a3a3a3',
|
400: '#a3a3a3',
|
||||||
500: '#737373',
|
500: '#737373',
|
||||||
600: '#525252',
|
600: '#525252',
|
||||||
700: '#404040',
|
700: '#404040',
|
||||||
800: '#262626',
|
800: '#262626',
|
||||||
900: '#171717',
|
900: '#171717',
|
||||||
},
|
},
|
||||||
yellow: {
|
yellow: {
|
||||||
50: '#fefce8',
|
50: '#fefce8',
|
||||||
100: '#fef9c3',
|
100: '#fef9c3',
|
||||||
400: '#facc15',
|
400: '#facc15',
|
||||||
500: '#eab308',
|
500: '#eab308',
|
||||||
},
|
},
|
||||||
orange: {
|
orange: {
|
||||||
100: '#ffedd5',
|
100: '#ffedd5',
|
||||||
200: '#fed7aa',
|
200: '#fed7aa',
|
||||||
300: '#fb713b',
|
300: '#fb713b',
|
||||||
400: '#fa5a15',
|
400: '#fa5a15',
|
||||||
500: '#e14d0b',
|
500: '#e14d0b',
|
||||||
600: '#ea580c',
|
600: '#ea580c',
|
||||||
},
|
},
|
||||||
red: {
|
red: {
|
||||||
400: '#f87171',
|
400: '#f87171',
|
||||||
500: '#ef4444',
|
500: '#ef4444',
|
||||||
},
|
},
|
||||||
zinc: {
|
zinc: {
|
||||||
200: '#e4e4e7',
|
200: '#e4e4e7',
|
||||||
400: '#a1a1aa',
|
400: '#a1a1aa',
|
||||||
500: '#71717a',
|
500: '#71717a',
|
||||||
600: '#52525b',
|
600: '#52525b',
|
||||||
700: '#3f3f46',
|
700: '#3f3f46',
|
||||||
800: '#27272a',
|
800: '#27272a',
|
||||||
900: '#18181b',
|
900: '#18181b',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
extend: {
|
extend: {
|
||||||
typography: ({ theme }) => ({
|
typography: ({ theme }) => ({
|
||||||
blog: {
|
blog: {
|
||||||
css: {
|
css: {
|
||||||
'--tw-prose-body': theme('colors.neutral[700]'),
|
'--tw-prose-body': theme('colors.neutral[700]'),
|
||||||
'--tw-prose-headings': theme('colors.neutral[900]'),
|
'--tw-prose-headings': theme('colors.neutral[900]'),
|
||||||
'--tw-prose-lead': theme('colors.neutral[700]'),
|
'--tw-prose-lead': theme('colors.neutral[700]'),
|
||||||
'--tw-prose-links': theme('colors.orange[300]'),
|
'--tw-prose-links': theme('colors.orange[300]'),
|
||||||
'--tw-prose-bold': theme('colors.neutral[900]'),
|
'--tw-prose-bold': theme('colors.neutral[900]'),
|
||||||
'--tw-prose-counters': theme('colors.neutral[600]'),
|
'--tw-prose-counters': theme('colors.neutral[600]'),
|
||||||
'--tw-prose-bullets': theme('colors.neutral[400]'),
|
'--tw-prose-bullets': theme('colors.neutral[400]'),
|
||||||
'--tw-prose-hr': theme('colors.neutral[300]'),
|
'--tw-prose-hr': theme('colors.neutral[300]'),
|
||||||
'--tw-prose-quotes': theme('colors.neutral[500]'),
|
'--tw-prose-quotes': theme('colors.neutral[500]'),
|
||||||
'--tw-prose-quote-borders': theme('colors.neutral[300]'),
|
'--tw-prose-quote-borders': theme('colors.neutral[300]'),
|
||||||
'--tw-prose-captions': theme('colors.neutral[700]'),
|
'--tw-prose-captions': theme('colors.neutral[700]'),
|
||||||
'--tw-prose-code': theme('colors.neutral[700]'),
|
'--tw-prose-code': theme('colors.neutral[700]'),
|
||||||
'--tw-prose-pre-code': theme('colors.neutral[200]'),
|
'--tw-prose-pre-code': theme('colors.neutral[200]'),
|
||||||
'--tw-prose-pre-bg': theme('colors.neutral[900]'),
|
'--tw-prose-pre-bg': theme('colors.neutral[900]'),
|
||||||
'--tw-prose-th-borders': theme('colors.neutral[300]'),
|
'--tw-prose-th-borders': theme('colors.neutral[300]'),
|
||||||
'--tw-prose-td-borders': theme('colors.neutral[200]'),
|
'--tw-prose-td-borders': theme('colors.neutral[200]'),
|
||||||
|
|
||||||
'--tw-prose-invert-body': theme('colors.neutral[400]'),
|
'--tw-prose-invert-body': theme('colors.neutral[400]'),
|
||||||
'--tw-prose-invert-headings': theme('colors.neutral[200]'),
|
'--tw-prose-invert-headings': theme('colors.neutral[200]'),
|
||||||
'--tw-prose-invert-lead': theme('colors.neutral[300]'),
|
'--tw-prose-invert-lead': theme('colors.neutral[300]'),
|
||||||
'--tw-prose-invert-links': theme('colors.neutral[300]'),
|
'--tw-prose-invert-links': theme('colors.neutral[300]'),
|
||||||
'--tw-prose-invert-bold': theme('colors.neutral[300]'),
|
'--tw-prose-invert-bold': theme('colors.neutral[300]'),
|
||||||
'--tw-prose-invert-counters': theme('colors.neutral[400]'),
|
'--tw-prose-invert-counters': theme('colors.neutral[400]'),
|
||||||
'--tw-prose-invert-bullets': theme('colors.neutral[600]'),
|
'--tw-prose-invert-bullets': theme('colors.neutral[600]'),
|
||||||
'--tw-prose-invert-hr': theme('colors.neutral[700]'),
|
'--tw-prose-invert-hr': theme('colors.neutral[700]'),
|
||||||
'--tw-prose-invert-quotes': theme('colors.neutral[500]'),
|
'--tw-prose-invert-quotes': theme('colors.neutral[500]'),
|
||||||
'--tw-prose-invert-quote-borders': theme('colors.neutral[500]'),
|
'--tw-prose-invert-quote-borders': theme('colors.neutral[500]'),
|
||||||
'--tw-prose-invert-captions': theme('colors.neutral[400]'),
|
'--tw-prose-invert-captions': theme('colors.neutral[400]'),
|
||||||
'--tw-prose-invert-code': theme('colors.neutral[300]'),
|
'--tw-prose-invert-code': theme('colors.neutral[300]'),
|
||||||
'--tw-prose-invert-pre-code': theme('colors.neutral[300]'),
|
'--tw-prose-invert-pre-code': theme('colors.neutral[300]'),
|
||||||
'--tw-prose-invert-pre-bg': 'rgb(0 0 0 / 50%)',
|
'--tw-prose-invert-pre-bg': 'rgb(0 0 0 / 50%)',
|
||||||
'--tw-prose-invert-th-borders': theme('colors.neutral[600]'),
|
'--tw-prose-invert-th-borders': theme('colors.neutral[600]'),
|
||||||
'--tw-prose-invert-td-borders': theme('colors.neutral[700]'),
|
'--tw-prose-invert-td-borders': theme('colors.neutral[700]'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DEFAULT: {
|
DEFAULT: {
|
||||||
css: {
|
css: {
|
||||||
blockquote: {
|
blockquote: {
|
||||||
fontStyle: 'normal',
|
fontStyle: 'normal',
|
||||||
quotes: 'none',
|
quotes: 'none',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
require('tailwindcss/nesting'),
|
require('tailwindcss/nesting'),
|
||||||
require('preline/plugin'),
|
require('preline/plugin'),
|
||||||
require('@tailwindcss/typography'),
|
require('@tailwindcss/typography'),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"extends": "astro/tsconfigs/strict",
|
"extends": "astro/tsconfigs/strict",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"baseUrl": "src",
|
"baseUrl": "src",
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"types": ["astro/client"],
|
"types": ["astro/client"],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@src/*": ["src/*"],
|
"@src/*": ["src/*"],
|
||||||
"@components/*": ["components/*"],
|
"@components/*": ["components/*"],
|
||||||
"@content/*": ["content/*"],
|
"@content/*": ["content/*"],
|
||||||
"@layouts/*": ["layouts/*"],
|
"@layouts/*": ["layouts/*"],
|
||||||
"@pages/*": ["pages/*"],
|
"@pages/*": ["pages/*"],
|
||||||
"@images/*": [
|
"@images/*": [
|
||||||
"images/*"
|
"images/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user