このコミットが含まれているのは:
@@ -8,7 +8,6 @@ class WikiPagesController < ApplicationController
|
|||||||
descriptions =
|
descriptions =
|
||||||
TagDescription.where(language_code: 'ja')
|
TagDescription.where(language_code: 'ja')
|
||||||
.includes(tag: :tag_names)
|
.includes(tag: :tag_names)
|
||||||
pages = unmigrated_wiki_pages.includes(tag_name: :tag)
|
|
||||||
|
|
||||||
tag_groups = TagGroup.all
|
tag_groups = TagGroup.all
|
||||||
if title.present?
|
if title.present?
|
||||||
@@ -19,12 +18,10 @@ class WikiPagesController < ApplicationController
|
|||||||
.where('tag_names.name LIKE ?', pattern).limit(20)
|
.where('tag_names.name LIKE ?', pattern).limit(20)
|
||||||
tag_groups = tag_groups.where(
|
tag_groups = tag_groups.where(
|
||||||
'CONCAT(?, name) LIKE ?', TagGroupRepr::PREFIX, pattern).limit(20)
|
'CONCAT(?, name) LIKE ?', TagGroupRepr::PREFIX, pattern).limit(20)
|
||||||
pages = pages.where('tag_names.name LIKE ?', pattern).limit(20)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
json = descriptions.map { |description| WikiPageRepr.tag_description(description) }
|
json = descriptions.map { |description| WikiPageRepr.tag_description(description) }
|
||||||
json.concat(tag_groups.map { WikiPageRepr.tag_group(_1) })
|
json.concat(tag_groups.map { WikiPageRepr.tag_group(_1) })
|
||||||
json.concat(WikiPageRepr.base(pages))
|
|
||||||
json = json.first(20) if title.present?
|
json = json.first(20) if title.present?
|
||||||
render json:
|
render json:
|
||||||
end
|
end
|
||||||
@@ -37,11 +34,7 @@ class WikiPagesController < ApplicationController
|
|||||||
tag_group = TagGroup.find_by(id: params[:id])
|
tag_group = TagGroup.find_by(id: params[:id])
|
||||||
return render_tag_group tag_group if tag_group
|
return render_tag_group tag_group if tag_group
|
||||||
|
|
||||||
page = WikiPage.joins(:tag_name)
|
render_wiki_page_or_404 nil
|
||||||
.includes(tag_name: :tag)
|
|
||||||
.find_by(id: params[:id])
|
|
||||||
|
|
||||||
render_wiki_page_or_404 page
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_by_title
|
def show_by_title
|
||||||
@@ -58,20 +51,14 @@ class WikiPagesController < ApplicationController
|
|||||||
|
|
||||||
return render_wiki_page_by_tag_description tag_description if tag_description
|
return render_wiki_page_by_tag_description tag_description if tag_description
|
||||||
|
|
||||||
page =
|
render_wiki_page_or_404 nil
|
||||||
WikiPage
|
|
||||||
.joins(:tag_name)
|
|
||||||
.includes(tag_name: :tag)
|
|
||||||
.find_by(tag_name: { name: title })
|
|
||||||
|
|
||||||
render_wiki_page_or_404 page
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def exists
|
def exists
|
||||||
return head :no_content if TagDescription.exists?(id: params[:id])
|
return head :no_content if TagDescription.exists?(id: params[:id])
|
||||||
return head :no_content if TagGroup.exists?(id: params[:id])
|
return head :no_content if TagGroup.exists?(id: params[:id])
|
||||||
|
|
||||||
head WikiPage.exists?(id: params[:id]) ? :no_content : :not_found
|
head :not_found
|
||||||
end
|
end
|
||||||
|
|
||||||
def exists_by_title
|
def exists_by_title
|
||||||
@@ -82,8 +69,7 @@ class WikiPagesController < ApplicationController
|
|||||||
end
|
end
|
||||||
return head :no_content if tag_description_by_title(title)
|
return head :no_content if tag_description_by_title(title)
|
||||||
|
|
||||||
exists = WikiPage.joins(:tag_name).exists?(tag_names: { name: title })
|
head :not_found
|
||||||
head exists ? :no_content : :not_found
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def diff
|
def diff
|
||||||
@@ -112,15 +98,7 @@ class WikiPagesController < ApplicationController
|
|||||||
'title' => "#{ TagGroupRepr::PREFIX }#{ tag_group.name }",
|
'title' => "#{ TagGroupRepr::PREFIX }#{ tag_group.name }",
|
||||||
'deprecated_at' => nil }
|
'deprecated_at' => nil }
|
||||||
else
|
else
|
||||||
page = WikiPage.joins(:tag_name).includes(tag_name: :tag).find(id)
|
raise 'お前やりませんねぇスギ!'
|
||||||
from_rev = from && page.wiki_revisions.find(from)
|
|
||||||
to_rev = to ? page.wiki_revisions.find(to) : page.current_revision
|
|
||||||
if ((from_rev && !from_rev.content?) || !to_rev&.content?)
|
|
||||||
return render_unprocessable_entity('差分を表示できない版です.')
|
|
||||||
end
|
|
||||||
from_body = from_rev&.body
|
|
||||||
to_body = to_rev.body
|
|
||||||
resource = WikiPageRepr.base(page)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -160,25 +138,17 @@ class WikiPagesController < ApplicationController
|
|||||||
status: :created)
|
status: :created)
|
||||||
end
|
end
|
||||||
|
|
||||||
page = if title.include?(':')
|
page =
|
||||||
ApplicationRecord.transaction do
|
if title.include?(':')
|
||||||
tag_name = TagName.find_or_create_by!(language_code: 'ja', name: title) do |name|
|
raise 'お前やりませんねぇスギ!'
|
||||||
name.script_code = 'Jpan'
|
else
|
||||||
name.primary_flg = true
|
tag_name = TagName.find_by(language_code: 'ja', name: title)
|
||||||
name.auto_generated = false
|
return render_unprocessable_entity field: :title unless tag_name&.tag
|
||||||
|
|
||||||
|
TagDescriptionOperation.create!(
|
||||||
|
tag: tag_name.tag, body:, created_by_user: current_user, message:)
|
||||||
end
|
end
|
||||||
|
|
||||||
Wiki::Commit.create_legacy_content!(
|
|
||||||
tag_name:, body:, created_by_user: current_user, message:)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
tag_name = TagName.find_by(language_code: 'ja', name: title)
|
|
||||||
return render_unprocessable_entity field: :title unless tag_name&.tag
|
|
||||||
|
|
||||||
TagDescriptionOperation.create!(
|
|
||||||
tag: tag_name.tag, body:, created_by_user: current_user, message:)
|
|
||||||
end
|
|
||||||
|
|
||||||
render json: WikiPageRepr.base(page), status: :created
|
render json: WikiPageRepr.base(page), status: :created
|
||||||
rescue ActiveRecord::RecordInvalid => e
|
rescue ActiveRecord::RecordInvalid => e
|
||||||
render_validation_error e.record
|
render_validation_error e.record
|
||||||
@@ -273,23 +243,7 @@ class WikiPagesController < ApplicationController
|
|||||||
return render json: tag_group_changes(TagGroupVersion.where(tag_group_id: tag_group.id))
|
return render json: tag_group_changes(TagGroupVersion.where(tag_group_id: tag_group.id))
|
||||||
end
|
end
|
||||||
|
|
||||||
pages = id ? WikiPage.where(id:) : unmigrated_wiki_pages
|
json = []
|
||||||
revisions = WikiRevision.joins(wiki_page: :tag_name)
|
|
||||||
.where(wiki_page_id: pages.select(:id))
|
|
||||||
.includes(:created_user, wiki_page: { tag_name: :tag })
|
|
||||||
.order(id: :desc).limit(200)
|
|
||||||
json = revisions.map { |rev|
|
|
||||||
{ revision_id: rev.id,
|
|
||||||
pred: rev.base_revision_id,
|
|
||||||
succ: nil,
|
|
||||||
wiki_page: { id: rev.wiki_page_id,
|
|
||||||
title: rev.wiki_page.title,
|
|
||||||
deprecated_at: rev.wiki_page.deprecated_at },
|
|
||||||
user: rev.created_user && { id: rev.created_user.id, name: rev.created_user.name },
|
|
||||||
kind: rev.kind,
|
|
||||||
message: rev.message,
|
|
||||||
timestamp: rev.created_at }
|
|
||||||
}
|
|
||||||
unless id
|
unless id
|
||||||
json.concat(tag_group_changes(TagGroupVersion.all))
|
json.concat(tag_group_changes(TagGroupVersion.all))
|
||||||
json.concat(description_changes(TagDescription.where(language_code: 'ja')))
|
json.concat(description_changes(TagDescription.where(language_code: 'ja')))
|
||||||
@@ -453,16 +407,6 @@ class WikiPagesController < ApplicationController
|
|||||||
.find_by(language_code: 'ja', tag_names: { language_code: 'ja', name: title })
|
.find_by(language_code: 'ja', tag_names: { language_code: 'ja', name: title })
|
||||||
end
|
end
|
||||||
|
|
||||||
def unmigrated_wiki_pages
|
|
||||||
descriptions = TagDescription.where(language_code: 'ja')
|
|
||||||
|
|
||||||
WikiPage
|
|
||||||
.joins(:tag_name)
|
|
||||||
.where.not('tag_names.name LIKE ?', "#{ TagGroupRepr::PREFIX }%")
|
|
||||||
.where('tag_names.tag_id IS NULL OR tag_names.tag_id NOT IN (?)',
|
|
||||||
descriptions.select(:tag_id))
|
|
||||||
end
|
|
||||||
|
|
||||||
def description_changes descriptions
|
def description_changes descriptions
|
||||||
versions = TagDescriptionVersion.where(language_code: 'ja',
|
versions = TagDescriptionVersion.where(language_code: 'ja',
|
||||||
tag_id: descriptions.select(:tag_id))
|
tag_id: descriptions.select(:tag_id))
|
||||||
|
|||||||
@@ -2,15 +2,21 @@ namespace :help do
|
|||||||
desc 'ヘルプ移行'
|
desc 'ヘルプ移行'
|
||||||
task migrate: :environment do
|
task migrate: :environment do
|
||||||
help_path = ENV.fetch('BTRC_HUB_HELP_PATH')
|
help_path = ENV.fetch('BTRC_HUB_HELP_PATH')
|
||||||
abort if help_path.blank?
|
abort 'ヘルプ置場指定してね.' if help_path.blank?
|
||||||
|
|
||||||
|
# ファイルがすでに存在する場合は通さなぃ.
|
||||||
|
abort 'ファイル消してね.' if Dir.glob("#{ help_path }/*").present?
|
||||||
|
|
||||||
# 現状物理削除はなぃはず
|
# 現状物理削除はなぃはず
|
||||||
pages =
|
pages =
|
||||||
WikiPage
|
WikiPage
|
||||||
.joins(:tag_name)
|
.joins(:tag_name)
|
||||||
.where(tag_names: { tag_id: nil })
|
.where(tag_names: { tag_id: nil, language_code: 'ja' })
|
||||||
.where.not("tag_names.name LIKE 'タググループ:%'")
|
.where.not("tag_names.name LIKE 'タググループ:%'")
|
||||||
|
|
||||||
|
puts "移行対象: #{pages.count} 件"
|
||||||
|
abort '移行対象がありません.' if pages.empty?
|
||||||
|
|
||||||
pages.find_each do |page|
|
pages.find_each do |page|
|
||||||
puts page.body
|
puts page.body
|
||||||
puts page.title
|
puts page.title
|
||||||
@@ -30,7 +36,8 @@ namespace :help do
|
|||||||
file_name += ".#{ (last_file_name_no || 1).to_i + 1 }"
|
file_name += ".#{ (last_file_name_no || 1).to_i + 1 }"
|
||||||
end
|
end
|
||||||
|
|
||||||
File.write "#{ help_path }/#{ identifier }/editions/ja/#{ file_name }.mdx", ver.body
|
body = "# #{ ver.title.sub(/^ヘルプ:/, '') }\n\n#{ ver.body }"
|
||||||
|
File.write "#{ help_path }/#{ identifier }/editions/ja/#{ file_name }.mdx", body
|
||||||
|
|
||||||
last_file_name = file_name
|
last_file_name = file_name
|
||||||
end
|
end
|
||||||
|
|||||||
+52
-80
@@ -15,7 +15,6 @@ import { Toaster } from '@/components/ui/toaster'
|
|||||||
import { apiPost, isApiError } from '@/lib/api'
|
import { apiPost, isApiError } from '@/lib/api'
|
||||||
import { applyClientAnimationMode,
|
import { applyClientAnimationMode,
|
||||||
applyClientAppearance,
|
applyClientAppearance,
|
||||||
type ClientAnimationMode,
|
|
||||||
fetchUserThemeSlots,
|
fetchUserThemeSlots,
|
||||||
fetchUserSettings,
|
fetchUserSettings,
|
||||||
getClientThemeMode,
|
getClientThemeMode,
|
||||||
@@ -25,7 +24,6 @@ import { applyClientAnimationMode,
|
|||||||
seedClientThemeMode } from '@/lib/settings'
|
seedClientThemeMode } from '@/lib/settings'
|
||||||
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
|
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
|
||||||
import { UnsavedChangesGuardProvider } from '@/lib/useUnsavedChangesGuard'
|
import { UnsavedChangesGuardProvider } from '@/lib/useUnsavedChangesGuard'
|
||||||
|
|
||||||
import { KeyboardShortcutsProvider } from '@/lib/useKeyboardShortcuts'
|
import { KeyboardShortcutsProvider } from '@/lib/useKeyboardShortcuts'
|
||||||
import DeerjikistDetailPage from '@/pages/deerjikists/DeerjikistDetailPage'
|
import DeerjikistDetailPage from '@/pages/deerjikists/DeerjikistDetailPage'
|
||||||
import MaterialBasePage from '@/pages/materials/MaterialBasePage'
|
import MaterialBasePage from '@/pages/materials/MaterialBasePage'
|
||||||
@@ -34,12 +32,13 @@ import MaterialHistoryPage from '@/pages/materials/MaterialHistoryPage'
|
|||||||
import MaterialListPage from '@/pages/materials/MaterialListPage'
|
import MaterialListPage from '@/pages/materials/MaterialListPage'
|
||||||
import MaterialNewPage from '@/pages/materials/MaterialNewPage'
|
import MaterialNewPage from '@/pages/materials/MaterialNewPage'
|
||||||
import MaterialSyncSuppressionsPage from '@/pages/materials/MaterialSyncSuppressionsPage'
|
import MaterialSyncSuppressionsPage from '@/pages/materials/MaterialSyncSuppressionsPage'
|
||||||
// import MaterialSearchPage from '@/pages/materials/MaterialSearchPage'
|
|
||||||
import MorePage from '@/pages/MorePage'
|
import MorePage from '@/pages/MorePage'
|
||||||
import GekanatorPage from '@/pages/GekanatorPage'
|
import GekanatorPage from '@/pages/GekanatorPage'
|
||||||
import NicoTagListPage from '@/pages/tags/NicoTagListPage'
|
import NicoTagListPage from '@/pages/tags/NicoTagListPage'
|
||||||
import NotFound from '@/pages/NotFound'
|
import NotFound from '@/pages/NotFound'
|
||||||
import TOSPage from '@/pages/TOSPage.mdx'
|
import TOSPage from '@/pages/TOSPage.mdx'
|
||||||
|
import HelpDetailPage from '@/pages/help/HelpDetailPage'
|
||||||
|
import HelpListPage from '@/pages/help/HelpListPage'
|
||||||
import PostDetailPage from '@/pages/posts/PostDetailPage'
|
import PostDetailPage from '@/pages/posts/PostDetailPage'
|
||||||
import PostHistoryPage from '@/pages/posts/PostHistoryPage'
|
import PostHistoryPage from '@/pages/posts/PostHistoryPage'
|
||||||
import PostNewPage from '@/pages/posts/PostNewPage'
|
import PostNewPage from '@/pages/posts/PostNewPage'
|
||||||
@@ -60,90 +59,60 @@ import WikiSearchPage from '@/pages/wiki/WikiSearchPage'
|
|||||||
|
|
||||||
import type { Dispatch, FC, SetStateAction } from 'react'
|
import type { Dispatch, FC, SetStateAction } from 'react'
|
||||||
|
|
||||||
|
import type { ClientAnimationMode } from '@/lib/settings'
|
||||||
import type { User } from '@/types'
|
import type { User } from '@/types'
|
||||||
|
|
||||||
|
|
||||||
const RouteTransitionWrapper = ({ animationMode, user, setUser }: {
|
const RouteTransitionWrapper = ({ animationMode, user, setUser }: {
|
||||||
animationMode: ClientAnimationMode
|
animationMode: ClientAnimationMode
|
||||||
user: User | null
|
user: User | null
|
||||||
setUser: Dispatch<SetStateAction<User | null>> }) => {
|
setUser: Dispatch<SetStateAction<User | null>> }) => {
|
||||||
const location = useLocation ()
|
const location = useLocation ()
|
||||||
|
|
||||||
|
const routes = (
|
||||||
|
<Routes location={location}>
|
||||||
|
<Route path="/" element={<Navigate to="/posts" replace/>}/>
|
||||||
|
<Route path="/posts" element={<PostListPage/>}/>
|
||||||
|
<Route path="/posts/new" element={<PostNewPage user={user}/>}/>
|
||||||
|
<Route path="/posts/search" element={<PostSearchPage/>}/>
|
||||||
|
<Route path="/posts/:id" element={<PostDetailRoute user={user}/>}/>
|
||||||
|
<Route path="/posts/changes" element={<PostHistoryPage/>}/>
|
||||||
|
<Route path="/tags" element={<TagListPage/>}/>
|
||||||
|
<Route path="/tags/:id" element={<TagDetailPage/>}/>
|
||||||
|
<Route path="/tags/:id/deerjikists" element={<DeerjikistDetailPage/>}/>
|
||||||
|
<Route path="/tags/nico" element={<NicoTagListPage user={user}/>}/>
|
||||||
|
<Route path="/nico/tags" element={<NicoTagListPage user={user}/>}/>
|
||||||
|
<Route path="/tags/changes" element={<TagHistoryPage/>}/>
|
||||||
|
<Route path="/theatres/:id" element={<TheatreDetailPage user={user}/>}/>
|
||||||
|
<Route path="/materials" element={<MaterialBasePage/>}>
|
||||||
|
<Route index element={<MaterialListPage/>}/>
|
||||||
|
<Route path="changes" element={<MaterialHistoryPage/>}/>
|
||||||
|
<Route path="new" element={<MaterialNewPage/>}/>
|
||||||
|
<Route path="suppressions" element={<MaterialSyncSuppressionsPage/>}/>
|
||||||
|
<Route path=":id" element ={<MaterialDetailPage/>}/>
|
||||||
|
</Route>
|
||||||
|
<Route path="/wiki" element={<WikiSearchPage/>}/>
|
||||||
|
<Route path="/wiki/:title" element={<WikiDetailPage/>}/>
|
||||||
|
<Route path="/wiki/new" element={<WikiNewPage user={user}/>}/>
|
||||||
|
<Route path="/wiki/:id/edit" element={<WikiEditPage user={user}/>}/>
|
||||||
|
<Route path="/wiki/:id/diff" element={<WikiDiffPage/>}/>
|
||||||
|
<Route path="/wiki/changes" element={<WikiHistoryPage/>}/>
|
||||||
|
<Route path="/help" element={<HelpListPage/>}/>
|
||||||
|
<Route path="/help/:identifier" element={<HelpDetailPage/>}/>
|
||||||
|
<Route path="/users/settings" element={<SettingPage user={user} setUser={setUser}/>}/>
|
||||||
|
<Route path="/settings" element={<Navigate to="/users/settings" replace/>}/>
|
||||||
|
<Route path="/tos" element={<TOSPage/>}/>
|
||||||
|
<Route path="/gekanator" element={<GekanatorPage user={user}/>}/>
|
||||||
|
<Route path="/more" element={<MorePage/>}/>
|
||||||
|
<Route path="*" element={<NotFound/>}/>
|
||||||
|
</Routes>)
|
||||||
|
|
||||||
if (animationMode === 'off')
|
if (animationMode === 'off')
|
||||||
{
|
return routes
|
||||||
return (
|
|
||||||
<Routes location={location}>
|
|
||||||
<Route path="/" element={<Navigate to="/posts" replace/>}/>
|
|
||||||
<Route path="/posts" element={<PostListPage/>}/>
|
|
||||||
<Route path="/posts/new" element={<PostNewPage user={user}/>}/>
|
|
||||||
<Route path="/posts/search" element={<PostSearchPage/>}/>
|
|
||||||
<Route path="/posts/:id" element={<PostDetailRoute user={user}/>}/>
|
|
||||||
<Route path="/posts/changes" element={<PostHistoryPage/>}/>
|
|
||||||
<Route path="/tags" element={<TagListPage/>}/>
|
|
||||||
<Route path="/tags/:id" element={<TagDetailPage/>}/>
|
|
||||||
<Route path="/tags/:id/deerjikists" element={<DeerjikistDetailPage/>}/>
|
|
||||||
<Route path="/tags/nico" element={<NicoTagListPage user={user}/>}/>
|
|
||||||
<Route path="/nico/tags" element={<NicoTagListPage user={user}/>}/>
|
|
||||||
<Route path="/tags/changes" element={<TagHistoryPage/>}/>
|
|
||||||
<Route path="/theatres/:id" element={<TheatreDetailPage user={user}/>}/>
|
|
||||||
<Route path="/materials" element={<MaterialBasePage/>}>
|
|
||||||
<Route index element={<MaterialListPage/>}/>
|
|
||||||
<Route path="changes" element={<MaterialHistoryPage/>}/>
|
|
||||||
<Route path="new" element={<MaterialNewPage/>}/>
|
|
||||||
<Route path="suppressions" element={<MaterialSyncSuppressionsPage/>}/>
|
|
||||||
<Route path=":id" element ={<MaterialDetailPage/>}/>
|
|
||||||
</Route>
|
|
||||||
<Route path="/wiki" element={<WikiSearchPage/>}/>
|
|
||||||
<Route path="/wiki/:title" element={<WikiDetailPage/>}/>
|
|
||||||
<Route path="/wiki/new" element={<WikiNewPage user={user}/>}/>
|
|
||||||
<Route path="/wiki/:id/edit" element={<WikiEditPage user={user}/>}/>
|
|
||||||
<Route path="/wiki/:id/diff" element={<WikiDiffPage/>}/>
|
|
||||||
<Route path="/wiki/changes" element={<WikiHistoryPage/>}/>
|
|
||||||
<Route path="/users/settings" element={<SettingPage user={user} setUser={setUser}/>}/>
|
|
||||||
<Route path="/settings" element={<Navigate to="/users/settings" replace/>}/>
|
|
||||||
<Route path="/tos" element={<TOSPage/>}/>
|
|
||||||
<Route path="/gekanator" element={<GekanatorPage user={user}/>}/>
|
|
||||||
<Route path="/more" element={<MorePage/>}/>
|
|
||||||
<Route path="*" element={<NotFound/>}/>
|
|
||||||
</Routes>)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatePresence mode="wait">
|
<AnimatePresence mode="wait">
|
||||||
<Routes location={location}>
|
{routes}
|
||||||
<Route path="/" element={<Navigate to="/posts" replace/>}/>
|
|
||||||
<Route path="/posts" element={<PostListPage/>}/>
|
|
||||||
<Route path="/posts/new" element={<PostNewPage user={user}/>}/>
|
|
||||||
<Route path="/posts/search" element={<PostSearchPage/>}/>
|
|
||||||
<Route path="/posts/:id" element={<PostDetailRoute user={user}/>}/>
|
|
||||||
<Route path="/posts/changes" element={<PostHistoryPage/>}/>
|
|
||||||
<Route path="/tags" element={<TagListPage/>}/>
|
|
||||||
<Route path="/tags/:id" element={<TagDetailPage/>}/>
|
|
||||||
<Route path="/tags/:id/deerjikists" element={<DeerjikistDetailPage/>}/>
|
|
||||||
<Route path="/tags/nico" element={<NicoTagListPage user={user}/>}/>
|
|
||||||
<Route path="/nico/tags" element={<NicoTagListPage user={user}/>}/>
|
|
||||||
<Route path="/tags/changes" element={<TagHistoryPage/>}/>
|
|
||||||
<Route path="/theatres/:id" element={<TheatreDetailPage user={user}/>}/>
|
|
||||||
<Route path="/materials" element={<MaterialBasePage/>}>
|
|
||||||
<Route index element={<MaterialListPage/>}/>
|
|
||||||
<Route path="changes" element={<MaterialHistoryPage/>}/>
|
|
||||||
<Route path="new" element={<MaterialNewPage/>}/>
|
|
||||||
<Route path="suppressions" element={<MaterialSyncSuppressionsPage/>}/>
|
|
||||||
<Route path=":id" element ={<MaterialDetailPage/>}/>
|
|
||||||
</Route>
|
|
||||||
<Route path="/wiki" element={<WikiSearchPage/>}/>
|
|
||||||
<Route path="/wiki/:title" element={<WikiDetailPage/>}/>
|
|
||||||
<Route path="/wiki/new" element={<WikiNewPage user={user}/>}/>
|
|
||||||
<Route path="/wiki/:id/edit" element={<WikiEditPage user={user}/>}/>
|
|
||||||
<Route path="/wiki/:id/diff" element={<WikiDiffPage/>}/>
|
|
||||||
<Route path="/wiki/changes" element={<WikiHistoryPage/>}/>
|
|
||||||
<Route path="/users/settings" element={<SettingPage user={user} setUser={setUser}/>}/>
|
|
||||||
<Route path="/settings" element={<Navigate to="/users/settings" replace/>}/>
|
|
||||||
<Route path="/tos" element={<TOSPage/>}/>
|
|
||||||
<Route path="/gekanator" element={<GekanatorPage user={user}/>}/>
|
|
||||||
<Route path="/more" element={<MorePage/>}/>
|
|
||||||
<Route path="*" element={<NotFound/>}/>
|
|
||||||
</Routes>
|
|
||||||
</AnimatePresence>)
|
</AnimatePresence>)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,9 +135,9 @@ const RoutedApp: FC = () => {
|
|||||||
() => (
|
() => (
|
||||||
animationMode === 'off'
|
animationMode === 'off'
|
||||||
? { duration: 0 }
|
? { duration: 0 }
|
||||||
: animationMode === 'reduced'
|
: (animationMode === 'reduced'
|
||||||
? { duration: .08, ease: 'linear' as const }
|
? { duration: .08, ease: 'linear' as const }
|
||||||
: { duration: .2, ease: 'easeOut' as const }
|
: { duration: .2, ease: 'easeOut' as const })
|
||||||
),
|
),
|
||||||
[animationMode],
|
[animationMode],
|
||||||
)
|
)
|
||||||
@@ -285,10 +254,12 @@ const RoutedApp: FC = () => {
|
|||||||
</DialogueProvider>)
|
</DialogueProvider>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const router = createBrowserRouter ([{
|
const router = createBrowserRouter ([{
|
||||||
path: '*',
|
path: '*',
|
||||||
element: <RoutedApp/> }])
|
element: <RoutedApp/> }])
|
||||||
|
|
||||||
|
|
||||||
const App: FC = () => (
|
const App: FC = () => (
|
||||||
<>
|
<>
|
||||||
<RouteBlockerOverlay/>
|
<RouteBlockerOverlay/>
|
||||||
@@ -296,4 +267,5 @@ const App: FC = () => (
|
|||||||
<RouterProvider router={router}/>
|
<RouterProvider router={router}/>
|
||||||
</>)
|
</>)
|
||||||
|
|
||||||
|
|
||||||
export default App
|
export default App
|
||||||
|
|||||||
@@ -82,6 +82,9 @@ export const menuOutline = (
|
|||||||
{ name: '履歴', to: `/wiki/changes?id=${ wikiId }`, visible: wikiPageFlg },
|
{ name: '履歴', to: `/wiki/changes?id=${ wikiId }`, visible: wikiPageFlg },
|
||||||
{ name: '編輯', to: `/wiki/${ wikiId || wikiTitle }/edit`,
|
{ name: '編輯', to: `/wiki/${ wikiId || wikiTitle }/edit`,
|
||||||
visible: wikiPageFlg && editable }] },
|
visible: wikiPageFlg && editable }] },
|
||||||
|
{ name: 'ヘルプ', to: '/help/home', base: '/help', subMenu: [
|
||||||
|
{ name: '検索', to: '/help' },
|
||||||
|
{ name: '全体履歴', to: '/help/changes' }] },
|
||||||
{ name: 'おたのしみ', visible: false, subMenu: [
|
{ name: 'おたのしみ', visible: false, subMenu: [
|
||||||
{ name: '上映会 (β)', to: '/theatres/1' },
|
{ name: '上映会 (β)', to: '/theatres/1' },
|
||||||
{ name: 'グカネータ (β)', to: '/gekanator' }] },
|
{ name: 'グカネータ (β)', to: '/gekanator' }] },
|
||||||
|
|||||||
+92
-35
@@ -1,49 +1,61 @@
|
|||||||
import { parse } from 'yaml'
|
import { parse as parseYAML } from 'yaml'
|
||||||
|
|
||||||
import type { ComponentType } from 'react'
|
import type { ComponentType } from 'react'
|
||||||
|
|
||||||
type ArticleLanguage = {
|
type ArticleLanguage = { currentEdition: string }
|
||||||
title: string
|
|
||||||
currentEdition: string }
|
|
||||||
|
|
||||||
type ArticleMeta = {
|
type ArticleMeta = {
|
||||||
identifier: string
|
identifier: string
|
||||||
canonicalLanguage: string
|
languages: Record<string, ArticleLanguage> }
|
||||||
languages: Record<string, ArticleLanguage> }
|
|
||||||
|
|
||||||
type MdxModule = { default: ComponentType }
|
type MDXModule = { default: ComponentType }
|
||||||
|
|
||||||
|
export type HelpEditionText = {
|
||||||
|
title: string
|
||||||
|
body: string }
|
||||||
|
|
||||||
|
export type HelpSearchEntry = HelpEditionText & {
|
||||||
|
identifier: string
|
||||||
|
edition: string }
|
||||||
|
|
||||||
|
const articleFiles = import.meta.glob<string> ('../../help/*/article.yml',
|
||||||
|
{ query: '?raw',
|
||||||
|
import: 'default',
|
||||||
|
eager: true })
|
||||||
|
|
||||||
|
const editionFiles = import.meta.glob<MDXModule> ('../../help/*/editions/*/*.mdx')
|
||||||
|
|
||||||
|
const rawEditionFiles = import.meta.glob<string> ('../../help/*/editions/*/*.mdx',
|
||||||
|
{ query: '?raw',
|
||||||
|
import: 'default' })
|
||||||
|
|
||||||
|
|
||||||
const articleFiles = import.meta.glob<string> (
|
const cameliseArticle = (
|
||||||
'../../help/*/article.yml',
|
value: { identifier: string
|
||||||
{ query: '?raw', import: 'default', eager: true, })
|
languages: Record<string, { ['current_edition']: string }> },
|
||||||
|
): ArticleMeta => (
|
||||||
const editionFiles = import.meta.glob<MdxModule> ('../../help/*/editions/*/*.mdx')
|
{ identifier: value.identifier,
|
||||||
|
|
||||||
|
|
||||||
const cameliseArticle = (value: {
|
|
||||||
identifier: string
|
|
||||||
canonical_language: string
|
|
||||||
languages: Record<string, {
|
|
||||||
title: string
|
|
||||||
current_edition: string }> }): ArticleMeta => (
|
|
||||||
{ identifier: value.identifier,
|
|
||||||
canonicalLanguage: value.canonical_language,
|
|
||||||
languages: Object.fromEntries (
|
languages: Object.fromEntries (
|
||||||
Object.entries (value.languages).map (([language, data]) => [
|
Object.entries (value.languages).map (([language, data]) => [
|
||||||
language,
|
language, { currentEdition: data['current_edition'] }])) })
|
||||||
{ title: data.title,
|
|
||||||
currentEdition: data.current_edition, }])) })
|
|
||||||
|
|
||||||
|
|
||||||
export const getHelpArticle = (identifier: string): ArticleMeta | undefined => {
|
const compareEditions = (a: string, b: string): number => {
|
||||||
const path = `../../help/${identifier}/article.yml`
|
const [dateA, revisionA = '1'] = a.split ('.')
|
||||||
|
const [dateB, revisionB = '1'] = b.split ('.')
|
||||||
|
|
||||||
|
return dateA.localeCompare (dateB) || Number (revisionA) - Number (revisionB)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const getHelpArticle = (identifier: string): ArticleMeta | null => {
|
||||||
|
const path = `../../help/${ identifier }/article.yml`
|
||||||
const source = articleFiles[path]
|
const source = articleFiles[path]
|
||||||
|
|
||||||
if (!(source))
|
if (!(source))
|
||||||
return undefined
|
return null
|
||||||
|
|
||||||
return cameliseArticle (parse (source))
|
return cameliseArticle (parseYAML (source))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -51,12 +63,12 @@ export const getHelpEditions = (
|
|||||||
identifier: string,
|
identifier: string,
|
||||||
language: string,
|
language: string,
|
||||||
): string[] => {
|
): string[] => {
|
||||||
const prefix = `../../help/${identifier}/editions/${language}/`
|
const prefix = `../../help/${ identifier }/editions/${ language }/`
|
||||||
|
|
||||||
return (Object.keys (editionFiles)
|
return (Object.keys (editionFiles)
|
||||||
.filter (path => path.startsWith (prefix))
|
.filter (path => path.startsWith (prefix))
|
||||||
.map (path => path.slice (prefix.length, -'.mdx'.length))
|
.map (path => path.slice (prefix.length, -'.mdx'.length))
|
||||||
.sort ())
|
.sort (compareEditions))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -64,12 +76,57 @@ export const getHelpEdition = async (
|
|||||||
identifier: string,
|
identifier: string,
|
||||||
language: string,
|
language: string,
|
||||||
edition: string,
|
edition: string,
|
||||||
): Promise<ComponentType | undefined> => {
|
): Promise<ComponentType | null> => {
|
||||||
const path = `../../help/${identifier}/editions/${language}/${edition}.mdx`
|
const path = `../../help/${ identifier }/editions/${ language }/${ edition }.mdx`
|
||||||
const loader = editionFiles[path]
|
const loader = editionFiles[path]
|
||||||
|
|
||||||
if (!(loader))
|
if (!(loader))
|
||||||
return undefined
|
return null
|
||||||
|
|
||||||
return (await loader ()).default
|
return (await loader ()).default
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const getHelpEditionText = async (
|
||||||
|
identifier: string,
|
||||||
|
language: string,
|
||||||
|
edition: string,
|
||||||
|
): Promise<HelpEditionText | null> => {
|
||||||
|
const path = `../../help/${ identifier }/editions/${ language }/${ edition }.mdx`
|
||||||
|
const loader = rawEditionFiles[path]
|
||||||
|
|
||||||
|
if (!(loader))
|
||||||
|
return null
|
||||||
|
|
||||||
|
const mdx = (await loader ()).replace (/^\uFEFF/, '')
|
||||||
|
const heading = mdx.match (/^# +([^\r\n]+)(?:\r?\n|$)/)
|
||||||
|
|
||||||
|
if (!(heading))
|
||||||
|
throw new Error (`Help MDX の先頭に H1 がありません: ${ path }`)
|
||||||
|
|
||||||
|
return { title: heading[1].trim (),
|
||||||
|
body: mdx.slice (heading[0].length) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const getHelpSearchEntries = async (
|
||||||
|
language: string,
|
||||||
|
): Promise<HelpSearchEntry[]> => {
|
||||||
|
const entries = await Promise.all (
|
||||||
|
Object.values (articleFiles).map (async source => {
|
||||||
|
const article = cameliseArticle (parseYAML (source))
|
||||||
|
const edition = article.languages[language]?.currentEdition
|
||||||
|
|
||||||
|
if (!(edition))
|
||||||
|
return null
|
||||||
|
|
||||||
|
const content = await getHelpEditionText (article.identifier, language, edition)
|
||||||
|
|
||||||
|
if (!(content))
|
||||||
|
throw new Error (`Help edition が見つかりません: ${ article.identifier }/${ edition }`)
|
||||||
|
|
||||||
|
return { identifier: article.identifier, edition, ...content }
|
||||||
|
}))
|
||||||
|
|
||||||
|
return entries.filter ((entry): entry is HelpSearchEntry => entry != null)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
|
import { Helmet } from 'react-helmet-async'
|
||||||
|
import { useLocation, useParams } from 'react-router-dom'
|
||||||
|
|
||||||
|
import PrefetchLink from '@/components/PrefetchLink'
|
||||||
|
import MainArea from '@/components/layout/MainArea'
|
||||||
|
import { SITE_TITLE } from '@/config'
|
||||||
|
import { getHelpArticle, getHelpEdition, getHelpEditionText, getHelpEditions } from '@/lib/help'
|
||||||
|
import NotFound from '@/pages/NotFound'
|
||||||
|
|
||||||
|
import type { FC, ComponentType } from 'react'
|
||||||
|
|
||||||
|
|
||||||
|
const HelpDetailPage: FC = () => {
|
||||||
|
const { identifier = '' } = useParams ()
|
||||||
|
const location = useLocation ()
|
||||||
|
|
||||||
|
// Locale 対応が入るまでは ja 固定
|
||||||
|
const language = 'ja'
|
||||||
|
|
||||||
|
const article = useMemo (
|
||||||
|
() => getHelpArticle (identifier),
|
||||||
|
[identifier])
|
||||||
|
|
||||||
|
const query = new URLSearchParams (location.search)
|
||||||
|
const requestedEdition = query.get ('edition')
|
||||||
|
|
||||||
|
const languageMeta = article?.languages[language]
|
||||||
|
|
||||||
|
const edition = requestedEdition ?? languageMeta?.currentEdition
|
||||||
|
|
||||||
|
const editions = useMemo (() => getHelpEditions (identifier, language),
|
||||||
|
[identifier, language])
|
||||||
|
|
||||||
|
const index = edition ? editions.indexOf (edition) : -1
|
||||||
|
const pred = index > 0 ? editions[index - 1] : undefined
|
||||||
|
const succ =
|
||||||
|
index >= 0 && index < editions.length - 1
|
||||||
|
? editions[index + 1]
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
const [loaded, setLoaded] = useState<{ identifier: string
|
||||||
|
language: string
|
||||||
|
edition: string
|
||||||
|
title: string
|
||||||
|
content: ComponentType | null } | null> (null)
|
||||||
|
|
||||||
|
useEffect (() => {
|
||||||
|
if (!(edition))
|
||||||
|
return
|
||||||
|
|
||||||
|
let active = true
|
||||||
|
|
||||||
|
void (async () => {
|
||||||
|
const component = await getHelpEdition (identifier, language, edition)
|
||||||
|
const text = await getHelpEditionText (identifier, language, edition)
|
||||||
|
|
||||||
|
if (active)
|
||||||
|
{
|
||||||
|
setLoaded ({ identifier,
|
||||||
|
language,
|
||||||
|
edition,
|
||||||
|
title: text?.title ?? '',
|
||||||
|
content: component })
|
||||||
|
}
|
||||||
|
}) ()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
active = false
|
||||||
|
}
|
||||||
|
}, [identifier, language, edition])
|
||||||
|
|
||||||
|
if (!(article)
|
||||||
|
|| !(languageMeta)
|
||||||
|
|| !(edition)
|
||||||
|
|| !(editions.includes (edition)))
|
||||||
|
return <NotFound/>
|
||||||
|
|
||||||
|
const current =
|
||||||
|
(loaded?.identifier === identifier
|
||||||
|
&& loaded?.language === language
|
||||||
|
&& loaded?.edition === edition)
|
||||||
|
? loaded
|
||||||
|
: null
|
||||||
|
|
||||||
|
if (current && (!(current.content) || !(current.title)))
|
||||||
|
return <NotFound/>
|
||||||
|
|
||||||
|
const Content = current?.content
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MainArea>
|
||||||
|
<Helmet>
|
||||||
|
<title>{current?.title || identifier} ヘルプ | {SITE_TITLE}</title>
|
||||||
|
</Helmet>
|
||||||
|
|
||||||
|
{requestedEdition && (
|
||||||
|
<div className="text-sm flex gap-3 items-center justify-center
|
||||||
|
border border-gray-700 rounded px-2 py-1 mb-4">
|
||||||
|
{pred
|
||||||
|
? (
|
||||||
|
<PrefetchLink
|
||||||
|
to={`/help/${ encodeURIComponent (identifier) }?edition=${ pred }`}>
|
||||||
|
< 古
|
||||||
|
</PrefetchLink>)
|
||||||
|
: '(最古)'}
|
||||||
|
|
||||||
|
<span>{edition}</span>
|
||||||
|
|
||||||
|
{succ
|
||||||
|
? (
|
||||||
|
<PrefetchLink
|
||||||
|
to={`/help/${ encodeURIComponent (identifier) }?edition=${ succ }`}>
|
||||||
|
新 >
|
||||||
|
</PrefetchLink>)
|
||||||
|
: '(最新)'}
|
||||||
|
</div>)}
|
||||||
|
|
||||||
|
<article className="prose dark:prose-invert mx-auto p-4">
|
||||||
|
{Content ? <Content/> : <div>Loading...</div>}
|
||||||
|
</article>
|
||||||
|
</MainArea>)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default HelpDetailPage
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
|
import { Helmet } from 'react-helmet-async'
|
||||||
|
|
||||||
|
import PrefetchLink from '@/components/PrefetchLink'
|
||||||
|
import FormField from '@/components/common/FormField'
|
||||||
|
import PageTitle from '@/components/common/PageTitle'
|
||||||
|
import MainArea from '@/components/layout/MainArea'
|
||||||
|
import { SITE_TITLE } from '@/config'
|
||||||
|
import { getHelpSearchEntries } from '@/lib/help'
|
||||||
|
import { dateString, inputClass } from '@/lib/utils'
|
||||||
|
|
||||||
|
import type { FormEvent, FC } from 'react'
|
||||||
|
|
||||||
|
import type { HelpSearchEntry } from '@/lib/help'
|
||||||
|
|
||||||
|
|
||||||
|
type SearchConditions = { title: string; text: string }
|
||||||
|
|
||||||
|
|
||||||
|
const HelpSearchPage: FC = () => {
|
||||||
|
const [title, setTitle] = useState ('')
|
||||||
|
const [text, setText] = useState ('')
|
||||||
|
const [conditions, setConditions] = useState<SearchConditions> ({ title: '', text: '' })
|
||||||
|
const [entries, setEntries] = useState<HelpSearchEntry[]> ([])
|
||||||
|
const [loading, setLoading] = useState (true)
|
||||||
|
const [error, setError] = useState<string | null> (null)
|
||||||
|
|
||||||
|
useEffect (() => {
|
||||||
|
let active = true
|
||||||
|
|
||||||
|
void getHelpSearchEntries ('ja')
|
||||||
|
.then (data => {
|
||||||
|
if (active)
|
||||||
|
setEntries (data)
|
||||||
|
})
|
||||||
|
.catch (err => {
|
||||||
|
if (active)
|
||||||
|
setError (err instanceof Error ? err.message : '取得に失敗しました.')
|
||||||
|
})
|
||||||
|
.finally (() => {
|
||||||
|
if (active)
|
||||||
|
setLoading (false)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => { active = false }
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const results = useMemo (() => {
|
||||||
|
const titleQuery = conditions.title.trim ().normalize ('NFKC').toLowerCase ()
|
||||||
|
const textQuery = conditions.text.trim ().normalize ('NFKC').toLowerCase ()
|
||||||
|
|
||||||
|
return entries.filter (entry => (
|
||||||
|
(!(titleQuery) || entry.title.normalize ('NFKC').toLowerCase ().includes (titleQuery))
|
||||||
|
&& (!(textQuery) || entry.body.normalize ('NFKC').toLowerCase ().includes (textQuery))))
|
||||||
|
.sort ((a, b) => a.title.localeCompare (b.title, 'ja'))
|
||||||
|
}, [entries, conditions])
|
||||||
|
|
||||||
|
const handleSearch = (ev: FormEvent<HTMLFormElement>) => {
|
||||||
|
ev.preventDefault ()
|
||||||
|
setConditions ({ title, text })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MainArea>
|
||||||
|
<Helmet>
|
||||||
|
<title>{`ヘルプ | ${SITE_TITLE}`}</title>
|
||||||
|
</Helmet>
|
||||||
|
|
||||||
|
<div className="max-w-xl">
|
||||||
|
<PageTitle>ヘルプ</PageTitle>
|
||||||
|
<form onSubmit={handleSearch} className="space-y-2">
|
||||||
|
<FormField label="タイトル">
|
||||||
|
{({ invalid }) => (
|
||||||
|
<input type="text"
|
||||||
|
aria-label="タイトル"
|
||||||
|
value={title}
|
||||||
|
onChange={e => setTitle (e.target.value)}
|
||||||
|
className={inputClass (invalid)}/>)}
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<FormField label="内容">
|
||||||
|
{({ invalid }) => (
|
||||||
|
<input type="text"
|
||||||
|
aria-label="内容"
|
||||||
|
value={text}
|
||||||
|
onChange={e => setText (e.target.value)}
|
||||||
|
className={inputClass (invalid)}/>)}
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<div className="py-3">
|
||||||
|
<button type="submit"
|
||||||
|
className="bg-blue-500 text-white px-4 py-2 rounded">
|
||||||
|
検索
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{loading
|
||||||
|
? <p className="mt-4">読み込み中...</p>
|
||||||
|
: (error
|
||||||
|
? <p className="mt-4 text-red-600" role="alert">{error}</p>
|
||||||
|
: (
|
||||||
|
<div className="mt-4">
|
||||||
|
<p className="mb-2 text-sm" aria-live="polite">{results.length} 件</p>
|
||||||
|
<table className="table-auto w-full border-collapse">
|
||||||
|
<thead className="border-b-2 border-black dark:border-white">
|
||||||
|
<tr>
|
||||||
|
<th className="p-2 text-left">タイトル</th>
|
||||||
|
<th className="p-2 text-left">最終更新</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{results.map (entry => {
|
||||||
|
const updatedAt = new Date(entry.edition.replace (/\..*$/, ''))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr key={entry.identifier}
|
||||||
|
className="even:bg-gray-100 dark:even:bg-gray-700">
|
||||||
|
<td className="p-2">
|
||||||
|
<PrefetchLink
|
||||||
|
to={`/help/${encodeURIComponent (entry.identifier)}`}>
|
||||||
|
{entry.title}
|
||||||
|
</PrefetchLink>
|
||||||
|
</td>
|
||||||
|
<td className="p-2">{dateString (updatedAt, 'hour')}</td>
|
||||||
|
</tr>)
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{results.length === 0 && <p className="mt-4">該当する記事がありません.</p>}
|
||||||
|
</div>))}
|
||||||
|
</MainArea>)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default HelpSearchPage
|
||||||
+14
-2
@@ -3,10 +3,22 @@ import react from '@vitejs/plugin-react'
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
|
|
||||||
|
const mdxPlugin = mdx ({ providerImportSource: '@/mdx-components' })
|
||||||
|
const originalTransform = mdxPlugin.transform
|
||||||
|
|
||||||
|
if (typeof originalTransform === 'function')
|
||||||
|
{
|
||||||
|
mdxPlugin.transform =
|
||||||
|
function (code, id)
|
||||||
|
{
|
||||||
|
return (/[?&](raw|url)(?:&|$)/.test (id)
|
||||||
|
? null
|
||||||
|
: originalTransform.call (this, code, id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// https://vite.dev/config/
|
|
||||||
export default defineConfig ({
|
export default defineConfig ({
|
||||||
plugins: [mdx ({ providerImportSource: '@/mdx-components' }), react ()],
|
plugins: [mdxPlugin, react ()],
|
||||||
resolve: { alias: { '@': path.resolve (__dirname, './src') } },
|
resolve: { alias: { '@': path.resolve (__dirname, './src') } },
|
||||||
server: { host: true,
|
server: { host: true,
|
||||||
port: 5173,
|
port: 5173,
|
||||||
|
|||||||
新しいイシューから参照
ユーザーをブロックする