環境#
システム:Windows 11
IDE:VSCode + Github Copilot Chat + Rust + Tauri プラグイン
環境:npm、Rust
npm create tauri-app@latest
を使用してプロジェクトフレームワークを構築しました。
以下のタイトルはすべて VSCode の Copilot チャットに入力されたテキストであり、その後 Copilot が出力したコードを更新して置き換えました。
ホームページを次のように変更します:左側にナビゲーションエリアを持ち、ツリー状のビューで異なる機能のページリンクを表示します。左側のナビゲーションエリアの機能メニュー項目をクリックすると、右側に対応する機能ページが読み込まれます。#
Note
ここでは 'App.tsx' が引用されています。
GitHub Copilot: この機能を実現するために、App.tsx
で以下の手順を実行する必要があります:
- ナビゲーションバーコンポーネントを作成し、ツリー状のビューで異なる機能のページリンクを表示します。
- ページナビゲーションを処理するために React Router を使用します。
- 右側のエリアに対応する機能ページを読み込みます。
まず、React Router をインストールします:
npm install react-router-dom
次に、App.tsx
ファイルを以下のように変更します:
import { useState } from "react";
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
import reactLogo from "./assets/react.svg";
import { invoke } from "@tauri-apps/api/core";
import "./App.css";
function Home() {
return <h2>ホームページ</h2>;
}
function Feature1() {
return <h2>機能1ページ</h2>;
}
function Feature2() {
return <h2>機能2ページ</h2>;
}
function App() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
async function greet() {
setGreetMsg(await invoke("greet", { name }));
}
return (
<Router>
<div className="container">
<nav className="sidebar">
<ul>
<li>
<Link to="/">ホーム</Link>
</li>
<li>
<Link to="/feature1">機能1</Link>
</li>
<li>
<Link to="/feature2">機能2</Link>
</li>
</ul>
</nav>
<div className="content">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/feature1" element={<Feature1 />} />
<Route path="/feature2" element={<Feature2 />} />
</Routes>
</div>
</div>
</Router>
);
}
export default App;
同時に、新しいレイアウトに適応するためにApp.css
ファイルを更新します:
.container {
display: flex;
}
.sidebar {
width: 200px;
background-color: #f0f0f0;
padding: 10px;
}
.content {
flex-grow: 1;
padding: 10px;
}
これにより、左側にナビゲーションバーが表示され、右側にはナビゲーションバーの選択に応じて対応する機能ページが読み込まれます。
Similar code found with 1 license type
独立した Home コンポーネントを作成します#
GitHub Copilot: はい、Home
コンポーネントを独立したファイルに抽出できます。以下は手順です:
- 新しいファイル
Home.tsx
を作成します。 Home
コンポーネントのコードをHome.tsx
ファイルに移動します。App.tsx
でHome
コンポーネントをインポートします。
まず、Home.tsx
ファイルを作成し、以下のコードを追加します:
// Home.tsx
import React from "react";
function Home() {
return <h2>ホームページ</h2>;
}
export default Home;
次に、App.tsx
ファイルを変更し、Home
コンポーネントをインポートします:
import { useState } from "react";
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
import reactLogo from "./assets/react.svg";
import { invoke } from "@tauri-apps/api/core";
import "./App.css";
import Home from "./Home";
function Feature1() {
return <h2>機能1ページ</h2>;
}
function Feature2() {
return <h2>機能2ページ</h2>;
}
function App() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
async function greet() {
setGreetMsg(await invoke("greet", { name }));
}
return (
<Router>
<div className="container">
<nav className="sidebar">
<ul>
<li>
<Link to="/">ホーム</Link>
</li>
<li>
<Link to="/feature1">機能1</Link>
</li>
<li>
<Link to="/feature2">機能2</Link>
</li>
</ul>
</nav>
<div className="content">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/feature1" element={<Feature1 />} />
<Route path="/feature2" element={<Feature2 />} />
</Routes>
</div>
</div>
</Router>
);
}
export default App;
これで、Home
コンポーネントが独立したファイルに抽出されました。
Similar code found with 1 license type
左側のナビゲーションバーを垂直に引き伸ばしてページ全体を占めるようにします#
Note
左側のナビゲーションバーはテキストを含む領域しか占有していないため、全体を占めるようにしたいです。
GitHub Copilot: 左側のナビゲーションバーをページ全体に垂直に引き伸ばすために、CSS スタイルを調整する必要があります。具体的な手順は以下の通りです:
App.css
ファイルを変更し、ナビゲーションバーがページの高さを占めるようにします。- コンテナの高さもページ全体を占めるようにします。
以下は更新されたApp.css
ファイルです:
/* App.css */
html, body, #root {
height: 100%;
margin: 0;
padding: 0;
}
.container {
display: flex;
height: 100%;
}
.sidebar {
width: 200px;
background-color: #f0f0f0;
padding: 10px;
height: 100%;
}
.content {
flex-grow: 1;
padding: 10px;
}
App.tsx
ファイルに以下のコードが含まれていることを確認してください:
import { useState } from "react";
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
import { invoke } from "@tauri-apps/api/core";
import "./App.css";
import Home from "./Home";
function Feature1() {
return <h2>機能1ページ</h2>;
}
function Feature2() {
return <h2>機能2ページ</h2>;
}
function App() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
async function greet() {
setGreetMsg(await invoke("greet", { name }));
}
return (
<Router>
<div className="container">
<nav className="sidebar">
<ul>
<li>
<Link to="/">ホーム</Link>
</li>
<li>
<Link to="/feature1">機能1</Link>
</li>
<li>
<Link to="/feature2">機能2</Link>
</li>
</ul>
</nav>
<div className="content">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/feature1" element={<Feature1 />} />
<Route path="/feature2" element={<Feature2 />} />
</Routes>
</div>
</div>
</Router>
);
}
export default App;
これにより、左側のナビゲーションバーがページの高さを垂直に引き伸ばすようになります。
Similar code found with 1 license type
右側のページと左側のナビゲーションバーを上部で揃えます#
Note
右側のコンテンツエリアがナビゲーションエリアの下に表示される状況が発生しました。4、5、6 は修正を試みたプロンプトであり、CSS の.container
スタイルが完全に置き換えられていないことがわかり、再度置き換えた後に正常に表示されました。
GitHub Copilot: 右側のページと左側のナビゲーションバーを上部で揃えるために、CSS スタイルを調整する必要があります。具体的な手順は以下の通りです:
- コンテナの高さがページ全体を占め、ナビゲーションバーとコンテンツエリアが上部から揃っていることを確認します。
- レイアウトが正しいことを確認するために
App.css
ファイルを更新します。
以下は更新されたApp.css
ファイルです:
/* App.css */
html, body, #root {
height: 100%;
margin: 0;
padding: 0;
}
.container {
display: flex;
height: 100%;
align-items: flex-start; /* 子要素を上部で揃える */
}
.sidebar {
width: 200px;
background-color: #f0f0f0;
padding: 10px;
height: 100%;
}
.content {
flex-grow: 1;
padding: 10px;
}
App.tsx
ファイルに以下のコードが含まれていることを確認してください:
import { useState } from "react";
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
import { invoke } from "@tauri-apps/api/core";
import "./App.css";
import Home from "./Home";
function Feature1() {
return <h2>機能1ページ</h2>;
}
function Feature2() {
return <h2>機能2ページ</h2>;
}
function App() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
async function greet() {
setGreetMsg(await invoke("greet", { name }));
}
return (
<Router>
<div className="container">
<nav className="sidebar">
<ul>
<li>
<Link to="/">ホーム</Link>
</li>
<li>
<Link to="/feature1">機能1</Link>
</li>
<li>
<Link to="/feature2">機能2</Link>
</li>
</ul>
</nav>
<div className="content">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/feature1" element={<Feature1 />} />
<Route path="/feature2" element={<Feature2 />} />
</Routes>
</div>
</div>
</Router>
);
}
export default App;
これにより、右側のページと左側のナビゲーションバーが上部で揃います。
Similar code found with 1 license type
.container 内の子要素を左右に配置します#
GitHub Copilot: .container
内の子要素を左右に配置するために、CSS スタイルを調整する必要があります。具体的な手順は以下の通りです:
.container
がflex
レイアウトを使用し、子要素が左右に配置されることを確認します。- 重複した
.container
スタイル定義を削除します。
以下は更新されたApp.css
ファイルです:
/* App.css */
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
html, body, #root {
height: 100%;
margin: 0;
padding: 0;
}
.container {
display: flex;
height: 100%;
align-items: flex-start; /* 子要素を上部で揃える */
}
.sidebar {
width: 200px;
background-color: #f0f0f0;
padding: 10px;
height: 100%;
}
.content {
flex-grow: 1;
padding: 10px;
}
App.tsx
ファイルに以下のコードが含まれていることを確認してください:
import { useState } from "react";
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
import { invoke } from "@tauri-apps/api/core";
import "./App.css";
import Home from "./Home";
function Feature1() {
return <h2>機能1ページ</h2>;
}
function Feature2() {
return <h2>機能2ページ</h2>;
}
function App() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
async function greet() {
setGreetMsg(await invoke("greet", { name }));
}
return (
<Router>
<div className="container">
<nav className="sidebar">
<ul>
<li>
<Link to="/">ホーム</Link>
</li>
<li>
<Link to="/feature1">機能1</Link>
</li>
<li>
<Link to="/feature2">機能2</Link>
</li>
</ul>
</nav>
<div className="content">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/feature1" element={<Feature1 />} />
<Route path="/feature2" element={<Feature2 />} />
</Routes>
</div>
</div>
</Router>
);
}
export default App;
これにより、.container
内の子要素が左右に配置され、上部で揃います。
Similar code found with 1 license type
ナビゲーションバーの背景色をシステムの背景色に設定します#
Note
システムテーマは暗色ですが、デフォルトの背景色は白色であり、このコードは実際には機能しませんでした。最終的に.sidebar
の背景色設定を手動で削除しました。
GitHub Copilot: ナビゲーションバーの背景色をシステムの背景色に設定するために、CSS のbackground-color
プロパティを使用し、その値をbackground
システムカラーに設定します。以下は更新されたApp.css
ファイルです:
/* App.css */
html, body, #root {
height: 100%;
margin: 0;
padding: 0;
}
.container {
display: flex;
height: 100%;
align-items: flex-start; /* 子要素を上部で揃える */
}
.sidebar {
width: 200px;
background-color: var(--system-background-color, #f0f0f0); /* システム背景色を使用 */
padding: 10px;
height: 100%;
}
.content {
flex-grow: 1;
padding: 10px;
}
.logo {
height: 6em;
}
上記のコードでは、var(--system-background-color, #f0f0f0)
を使用して背景色を設定しています。システム背景色が未定義の場合は、デフォルトの色#f0f0f0
を使用します。必要に応じてデフォルトの色を調整できます。
Feature1 を独立したモジュールとして Features サブディレクトリに移動します#
GitHub Copilot: はい、Feature1
コンポーネントを独立したファイルに抽出し、Features
サブディレクトリに配置できます。以下は手順です:
- 新しいディレクトリ
Features
を作成します。 Features
ディレクトリ内に新しいファイルFeature1.tsx
を作成します。Feature1
コンポーネントのコードをFeature1.tsx
ファイルに移動します。App.tsx
でFeature1
コンポーネントをインポートします。
まず、Features/Feature1.tsx
ファイルを作成し、以下のコードを追加します:
// Features/Feature1.tsx
import React from "react";
function Feature1() {
return <h2>機能1ページ</h2>;
}
export default Feature1;
次に、App.tsx
ファイルを変更し、Feature1
コンポーネントをインポートします:
import { useState } from "react";
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
import { invoke } from "@tauri-apps/api/core";
import "./App.css";
import Home from "./Home";
import Feature1 from "./Features/Feature1";
function Feature2() {
return <h2>機能2ページ</h2>;
}
function App() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
async function greet() {
setGreetMsg(await invoke("greet", { name }));
}
return (
<Router>
<div className="container">
<nav className="sidebar">
<ul>
<li>
<Link to="/">ホーム</Link>
</li>
<li>
<Link to="/feature1">機能1</Link>
</li>
<li>
<Link to="/feature2">機能2</Link>
</li>
</ul>
</nav>
<div className="content">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/feature1" element={<Feature1 />} />
<Route path="/feature2" element={<Feature2 />} />
</Routes>
</div>
</div>
</Router>
);
}
export default App;
これにより、Feature1
コンポーネントがFeatures
サブディレクトリの独立したモジュールに移動されました。
Similar code found with 1 license type
同様に Feature2 を独立したファイルに移動します#
GitHub Copilot: はい、Feature2
コンポーネントを独立したファイルに抽出し、Features
サブディレクトリに配置できます。以下は手順です:
Features
ディレクトリ内に新しいファイルFeature2.tsx
を作成します。Feature2
コンポーネントのコードをFeature2.tsx
ファイルに移動します。App.tsx
でFeature2
コンポーネントをインポートします。
まず、Features/Feature2.tsx
ファイルを作成し、以下のコードを追加します:
// Features/Feature2.tsx
import React from "react";
function Feature2() {
return <h2>機能2ページ</h2>;
}
export default Feature2;
次に、App.tsx
ファイルを変更し、Feature2
コンポーネントをインポートします:
import { useState } from "react";
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
import { invoke } from "@tauri-apps/api/core";
import "./App.css";
import Home from "./Home";
import Feature1 from "./Features/Feature1";
import Feature2 from "./Features/Feature2";
function App() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
async function greet() {
setGreetMsg(await invoke("greet", { name }));
}
return (
<Router>
<div className="container">
<nav className="sidebar">
<ul>
<li>
<Link to="/">ホーム</Link>
</li>
<li>
<Link to="/feature1">機能1</Link>
</li>
<li>
<Link to="/feature2">機能2</Link>
</li>
</ul>
</nav>
<div className="content">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/feature1" element={<Feature1 />} />
<Route path="/feature2" element={<Feature2 />} />
</Routes>
</div>
</div>
</Router>
);
}
export default App;
これにより、Feature2
コンポーネントがFeatures
サブディレクトリの独立したモジュールに移動されました。
Similar code found with 1 license type
ページのデフォルトにスクロールバーが表示されないように高さを調整します#
Note
デフォルトのコンテンツエリアにスクロールバーが表示されました。
GitHub Copilot: ページの高さを調整してスクロールバーが表示されないようにするために、コンテナの高さとコンテンツの高さが正しく設定されていることを確認する必要があります。overflow: hidden
を使用してスクロールバーの表示を防ぎ、コンテンツエリアの高さがビューポートの高さを超えないようにします。
以下は更新されたApp.css
ファイルです:
/* App.css */
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
html, body, #root {
height: 100%;
margin: 0;
padding: 0;
overflow: hidden; /* スクロールバーの表示を防ぐ */
}
.container {
display: flex;
height: 100%;
align-items: flex-start; /* 子要素を上部で揃える */
}
.sidebar {
width: 200px;
padding: 10px;
height: 100%;
background-color: var(--system-background-color, #f0f0f0); /* システム背景色を使用 */
}
.content {
flex-grow: 1;
padding: 10px;
height: 100%;
overflow: auto; /* コンテンツエリアをスクロール可能にする */
}
App.tsx
ファイルに以下のコードが含まれていることを確認してください:
import { useState } from "react";
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
import { invoke } from "@tauri-apps/api/core";
import "./App.css";
import Home from "./Home";
import Feature1 from "./Features/Feature1";
import Feature2 from "./Features/Feature2";
function App() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
async function greet() {
setGreetMsg(await invoke("greet", { name }));
}
return (
<Router>
<div className="container">
<nav className="sidebar">
<ul>
<li>
<Link to="/">ホーム</Link>
</li>
<li>
<Link to="/feature1">機能1</Link>
</li>
<li>
<Link to="/feature2">機能2</Link>
</li>
</ul>
</nav>
<div className="content">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/feature1" element={<Feature1 />} />
<Route path="/feature2" element={<Feature2 />} />
</Routes>
</div>
</div>
</Router>
);
}
export default App;
これにより、ページの高さが調整され、スクロールバーが表示されないようになります。コンテンツエリアはスクロール可能です。
Similar code found with 1 license type
greetMsg の段落を新しい行に表示します#
Note
ここでの理由は、例の中で greet のインタラクションを保持するために、手動でコードをApp.tsx
からHome.tsx
に移動しましたが、<p>
タグが機能せず、テキスト出力エリアが同じ行に表示されました。次のプロンプトで、<div>
タグを使用して問題を解決しました。
GitHub Copilot: greetMsg
を新しい行に表示するために、段落 (<p>
) タグに入れることができます。以下は更新されたHome.tsx
ファイルです:
// Home.tsx
import React from "react";
function Home({ greetMsg }: { greetMsg: string }) {
return (
<div>
<h2>ホームページ</h2>
<p>{greetMsg}</p>
</div>
);
}
export default Home;
同時に、App.tsx
でgreetMsg
をHome
コンポーネントのプロパティとして渡すことを確認します:
import { useState } from "react";
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
import { invoke } from "@tauri-apps/api/core";
import "./App.css";
import Home from "./Home";
import Feature1 from "./Features/Feature1";
import Feature2 from "./Features/Feature2";
function App() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
async function greet() {
setGreetMsg(await invoke("greet", { name }));
}
return (
<Router>
<div className="container">
<nav className="sidebar">
<ul>
<li>
<Link to="/">ホーム</Link>
</li>
<li>
<Link to="/feature1">機能1</Link>
</li>
<li>
<Link to="/feature2">機能2</Link>
</li>
</ul>
</nav>
<div className="content">
<Routes>
<Route path="/" element={<Home greetMsg={greetMsg} />} />
<Route path="/feature1" element={<Feature1 />} />
<Route path="/feature2" element={<Feature2 />} />
</Routes>
</div>
</div>
</Router>
);
}
export default App;
これにより、greetMsg
がHome
コンポーネントの新しい行に表示されます。
Similar code found with 1 license type
greetMsg は App から渡す必要はありません#
GitHub Copilot: はい、Home
コンポーネント内で直接greetMsg
を定義し、App
から渡す必要はありません。以下は更新されたHome.tsx
ファイルです:
// Home.tsx
import React, { useState } from "react";
import { invoke } from "@tauri-apps/api/core";
function Home() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
async function greet() {
setGreetMsg(await invoke("greet", { name }));
}
return (
<div>
<h2>ホームページ</h2>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="名前を入力してください"
/>
<button onClick={greet}>挨拶</button>
<p>{greetMsg}</p>
</div>
);
}
export default Home;
同時に、App.tsx
ファイルは変更する必要がありません。なぜなら、greetMsg
はもはやApp
から渡す必要がないからです:
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
import "./App.css";
import Home from "./Home";
import Feature1 from "./Features/Feature1";
import Feature2 from "./Features/Feature2";
function App() {
return (
<Router>
<div className="container">
<nav className="sidebar">
<ul>
<li>
<Link to="/">ホーム</Link>
</li>
<li>
<Link to="/feature1">機能1</Link>
</li>
<li>
<Link to="/feature2">機能2</Link>
</li>
</ul>
</nav>
<div className="content">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/feature1" element={<Feature1 />} />
<Route path="/feature2" element={<Feature2 />} />
</Routes>
</div>
</div>
</Router>
);
}
export default App;
これにより、greetMsg
はHome
コンポーネント内で定義され、使用されます。
Similar code found with 2 license types
Feature1 を ClipBoard にリネームします#
GitHub Copilot: はい、Feature1
コンポーネントをClipBoard
にリネームできます。以下は手順です:
Feature1.tsx
ファイルの名前をFeatures/ClipBoard.tsx
に変更します。- コンポーネント名を
ClipBoard
に更新します。 App.tsx
でClipBoard
コンポーネントをインポートします。
まず、Feature1.tsx
ファイルの名前をFeatures/ClipBoard.tsx
に変更し、ファイル内容を以下のように更新します:
// Features/ClipBoard.tsx
import React from "react";
function ClipBoard() {
return <h2>クリップボードページ</h2>;
}
export default ClipBoard;
次に、App.tsx
ファイルを変更し、ClipBoard
コンポーネントをインポートします:
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
import "./App.css";
import Home from "./Home";
import ClipBoard from "./Features/ClipBoard";
import Feature2 from "./Features/Feature2";
function App() {
return (
<Router>
<div className="container">
<nav className="sidebar">
<ul>
<li>
<Link to="/">ホーム</Link>
</li>
<li>
<Link to="/clipboard">クリップボード</Link>
</li>
<li>
<Link to="/feature2">機能2</Link>
</li>
</ul>
</nav>
<div className="content">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/clipboard" element={<ClipBoard />} />
<Route path="/feature2" element={<Feature2 />} />
</Routes>
</div>
</div>
</Router>
);
}
export default App;
これにより、Feature1
コンポーネントがClipBoard
にリネームされ、App.tsx
で正しくインポートされ、使用されます。
クリップボードページに最近のクリップボード内容を表示し、時間の逆順で並べ、最大 100 件を表示します#
GitHub Copilot: クリップボードページに最近のクリップボード内容を表示し、時間の逆順で並べ、最大 100 件を表示するために、以下の手順が必要です:
- クリップボード内容を保存するための状態を作成します。
useEffect
フックを使用してクリップボード内容を取得するシミュレーションを行います。- クリップボード内容を時間の逆順で並べ、最大 100 件を制限します。
以下は更新されたClipBoard.tsx
ファイルです:
// Features/ClipBoard.tsx
import React, { useState, useEffect } from "react";
interface ClipboardItem {
content: string;
timestamp: number;
}
function ClipBoard() {
const [clipboardItems, setClipboardItems] = useState<ClipboardItem[]>([]);
useEffect(() => {
// クリップボード内容を取得するシミュレーション
const fetchClipboardItems = () => {
const items = [
{ content: "アイテム1", timestamp: Date.now() - 1000 },
{ content: "アイテム2", timestamp: Date.now() - 2000 },
// さらにシミュレーションデータを追加
];
setClipboardItems(items);
};
fetchClipboardItems();
}, []);
// 時間の逆順で並べ、最大100件を制限
const sortedItems = clipboardItems
.sort((a, b) => b.timestamp - a.timestamp)
.slice(0, 100);
return (
<div>
<h2>クリップボードページ</h2>
<ul>
{sortedItems.map((item, index) => (
<li key={index}>
<p>{item.content}</p>
<small>{new Date(item.timestamp).toLocaleString()}</small>
</li>
))}
</ul>
</div>
);
}
export default ClipBoard;
この実装では、以下のことを行います:
- クリップボード内容項目を表すために
ClipboardItem
インターフェースを定義します。 useState
フックを使用してクリップボード内容を保存します。useEffect
フックを使用してクリップボード内容を取得するシミュレーションを行い、状態に保存します。- クリップボード内容を時間の逆順で並べ、最大 100 件を制限します。
- コンポーネント内でクリップボード内容リストをレンダリングします。
必要に応じて、シミュレーションデータ部分を実際のクリップボード内容を取得するように置き換えることができます。
1 秒ごとにクリップボードを定期的に読み取り、新しい内容がある場合はクリップボードの内容リストに追加します#
Note
ここでは、src-tauri/capabilities/default.json
にクリップボードの読み書き権限を手動で追加する必要があります。以降のファイル操作も同様です。また、copilot コード内のimport
は Tauri v1 の形式で出力されているため、手動で v2 の形式に調整する必要があります。
GitHub Copilot: 1 秒ごとにクリップボードを定期的に読み取り、新しい内容がある場合はクリップボードの内容リストに追加するために、setInterval
を使用してクリップボード内容を定期的に読み取り、useEffect
内で処理を行います。
以下は更新されたClipBoard.tsx
ファイルです:
// Features/ClipBoard.tsx
import React, { useState, useEffect } from "react";
import { readText } from "@tauri-apps/plugin-clipboard";
interface ClipboardItem {
content: string;
timestamp: number;
}
function ClipBoard() {
const [clipboardItems, setClipboardItems] = useState<ClipboardItem[]>([]);
const [lastContent, setLastContent] = useState<string>("");
useEffect(() => {
const interval = setInterval(async () => {
const content = await readText();
if (content && content !== lastContent) {
setClipboardItems((prevItems) => [
{ content, timestamp: Date.now() },
...prevItems,
]);
setLastContent(content);
}
}, 1000);
return () => clearInterval(interval);
}, [lastContent]);
// 時間の逆順で並べ、最大100件を制限
const sortedItems = clipboardItems.slice(0, 100);
return (
<div>
<h2>クリップボードページ</h2>
<ul>
{sortedItems.map((item, index) => (
<li key={index}>
<p>{item.content}</p>
<small>{new Date(item.timestamp).toLocaleString()}</small>
</li>
))}
</ul>
</div>
);
}
export default ClipBoard;
この実装では、以下のことを行います:
useState
フックを使用してクリップボード内容と最後に読み取った内容を保存します。useEffect
フックを使用して 1 秒ごとにクリップボード内容を読み取るタイマーを設定します。- 新しい内容が最後に読み取った内容と異なる場合、クリップボード内容リストに追加し、最後に読み取った内容を更新します。
- 時間の逆順で並べ、最大 100 件を制限します。
- コンポーネント内でクリップボード内容リストをレンダリングします。
これにより、1 秒ごとにクリップボード内容を読み取り、新しい内容がある場合はクリップボードの内容リストに追加されます。
時間を内容の上に表示します#
GitHub Copilot: 時間を内容の上に表示するために、ClipBoard.tsx
ファイル内のレンダリング順序を調整する必要があります。以下は更新されたコードです:
// Features/ClipBoard.tsx
import React, { useState, useEffect } from "react";
import { readText } from "@tauri-apps/plugin-clipboard";
interface ClipboardItem {
content: string;
timestamp: number;
}
function ClipBoard() {
const [clipboardItems, setClipboardItems] = useState<ClipboardItem[]>([]);
const [lastContent, setLastContent] = useState<string>("");
useEffect(() => {
const interval = setInterval(async () => {
const content = await readText();
if (content && content !== lastContent) {
setClipboardItems((prevItems) => [
{ content, timestamp: Date.now() },
...prevItems,
]);
setLastContent(content);
}
}, 1000);
return () => clearInterval(interval);
}, [lastContent]);
// 時間の逆順で並べ、最大100件を制限
const sortedItems = clipboardItems.slice(0, 100);
return (
<div>
<h2>クリップボードページ</h2>
<ul>
{sortedItems.map((item, index) => (
<li key={index}>
<small>{new Date(item.timestamp).toLocaleString()}</small>
<p>{item.content}</p>
</li>
))}
</ul>
</div>
);
}
export default ClipBoard;
この実装では、時間 (<small>{new Date(item.timestamp).toLocaleString()}</small>
) を内容 (<p>{item.content}</p>
) の上に配置しました。これにより、時間が内容の上に表示されます。
各項目に「コピー」と「削除」のテキストリンクを提供し、コピーをクリックすると再度クリップボードにコピーし、削除をクリックするとその項目をリストから削除します#
GitHub Copilot: 各項目に「コピー」と「削除」のテキストリンクを提供するために、以下の手順が必要です:
- 「コピー」と「削除」ボタンを追加します。
- コピー機能を実装し、内容を再度クリップボードにコピーします。
- 削除機能を実装し、リストからその項目を削除します。
以下は更新されたClipBoard.tsx
ファイルです:
// Features/ClipBoard.tsx
import React, { useState, useEffect } from "react";
import { readText, writeText } from "@tauri-apps/plugin-clipboard-manager";
interface ClipboardItem {
content: string;
timestamp: number;
}
function ClipBoard() {
const [clipboardItems, setClipboardItems] = useState<ClipboardItem[]>([]);
const [lastContent, setLastContent] = useState<string>("");
useEffect(() => {
const interval = setInterval(async () => {
const content = await readText();
if (content && content !== lastContent) {
setClipboardItems((prevItems) => [
{ content, timestamp: Date.now() },
...prevItems,
]);
setLastContent(content);
}
}, 1000);
return () => clearInterval(interval);
}, [lastContent]);
const handleCopy = async (content: string) => {
await writeText(content);
};
const handleDelete = (timestamp: number) => {
setClipboardItems((prevItems) =>
prevItems.filter((item) => item.timestamp !== timestamp)
);
};
// 時間の逆順で並べ、最大100件を制限
const sortedItems = clipboardItems.slice(0, 100);
return (
<div>
<h2>クリップボードページ</h2>
<ul>
{sortedItems.map((item, index) => (
<li key={index}>
<small>{new Date(item.timestamp).toLocaleString()}</small>
<p>{item.content}</p>
<a href="#" onClick={() => handleCopy(item.content)}>コピー</a> |
<a href="#" onClick={() => handleDelete(item.timestamp)}>削除</a>
</li>
))}
</ul>
</div>
);
}
export default ClipBoard;
この実装では、以下のことを行います:
- 「コピー」と「削除」ボタンを追加しました。
handleCopy
関数を実装し、内容を再度クリップボードにコピーします。handleDelete
関数を実装し、リストからその項目を削除します。- 各リスト項目に「コピー」と「削除」リンクを追加し、対応するイベント処理関数をバインドします。
これにより、各項目に「コピー」と「削除」のテキストリンクが提供され、コピーをクリックすると再度クリップボードにコピーされ、削除をクリックするとリストからその項目が削除されます。
リストに内容のテキストと内容の md5 値を同時に保存し、新しい内容の md5 値が既存の内容のテキストと一致する場合は、単に時間を更新し、新しいレコードを追加しないようにします#
Note
コピー後に新しいレコードが生成されるため、md5 を使用して一意性を確保し、表示を行います。
GitHub Copilot: リストに内容のテキストと内容の MD5 値を同時に保存し、新しい内容の MD5 値が既存の内容のテキストと一致する場合は、単に時間を更新し、新しいレコードを追加しないようにするために、以下の手順が必要です:
crypto-js
ライブラリをインストールして MD5 値を計算します。ClipboardItem
インターフェースを更新して MD5 値を含めます。- クリップボード内容を読み取る際に MD5 値を計算し、既存の MD5 値と一致するかどうかを確認します。
まず、crypto-js
ライブラリをインストールします:
npm install crypto-js
次に、ClipBoard.tsx
ファイルを更新します:
// Features/ClipBoard.tsx
import React, { useState, useEffect } from "react";
import { readText, writeText } from "@tauri-apps/plugin-clipboard-manager";
import CryptoJS from "crypto-js";
interface ClipboardItem {
content: string;
md5: string;
timestamp: number;
}
function ClipBoard() {
const [clipboardItems, setClipboardItems] = useState<ClipboardItem[]>([]);
const [lastContent, setLastContent] = useState<string>("");
useEffect(() => {
const interval = setInterval(async () => {
const content = await readText();
if (content && content !== lastContent) {
const md5 = CryptoJS.MD5(content).toString();
setClipboardItems((prevItems) => {
const existingItem = prevItems.find(item => item.md5 === md5);
if (existingItem) {
return prevItems.map(item =>
item.md5 === md5 ? { ...item, timestamp: Date.now() } : item
);
} else {
return [{ content, md5, timestamp: Date.now() }, ...prevItems];
}
});
setLastContent(content);
}
}, 1000);
return () => clearInterval(interval);
}, [lastContent]);
const handleCopy = async (content: string) => {
await writeText(content);
};
const handleDelete = (timestamp: number) => {
setClipboardItems((prevItems) =>
prevItems.filter((item) => item.timestamp !== timestamp)
);
};
// 時間の逆順で並べ、最大100件を制限
const sortedItems = clipboardItems.slice(0, 100);
return (
<div>
<h2>クリップボードページ</h2>
<ul>
{sortedItems.map((item, index) => (
<li key={index}>
<small>{new Date(item.timestamp).toLocaleString()}</small>
<p>{item.content}</p>
<a href="#" onClick={() => handleCopy(item.content)}>コピー</a> |
<a href="#" onClick={() => handleDelete(item.timestamp)}>削除</a>
</li>
))}
</ul>
</div>
);
}
export default ClipBoard;
この実装では、以下のことを行います:
crypto-js
ライブラリをインストールして MD5 値を計算します。ClipboardItem
インターフェースを更新してmd5
属性を含めます。- クリップボード内容を読み取る際に MD5 値を計算し、既存の MD5 値と一致するかどうかを確認します。一致する場合は時間を更新し、一致しない場合は新しいレコードを追加します。
同じ内容の更新後、リストを再度時間でソートします#
GitHub Copilot: 同じ内容の更新後、リストを再度時間でソートするために、更新されたタイムスタンプ後にリストをソートする必要があります。以下は更新されたClipBoard.tsx
ファイルです:
// Features/ClipBoard.tsx
import React, { useState, useEffect } from "react";
import { readText, writeText } from "@tauri-apps/plugin-clipboard-manager";
import CryptoJS from "crypto-js";
interface ClipboardItem {
content: string;
md5: string;
timestamp: number;
}
function ClipBoard() {
const [clipboardItems, setClipboardItems] = useState<ClipboardItem[]>([]);
const [lastContent, setLastContent] = useState<string>("");
useEffect(() => {
const interval = setInterval(async () => {
const content = await readText();
if (content && content !== lastContent) {
const md5 = CryptoJS.MD5(content).toString();
setClipboardItems((prevItems) => {
const existingItem = prevItems.find(item => item.md5 === md5);
if (existingItem) {
const updatedItems = prevItems.map(item =>
item.md5 === md5 ? { ...item, timestamp: Date.now() } : item
);
return updatedItems.sort((a, b) => b.timestamp - a.timestamp);
} else {
const newItems = [{ content, md5, timestamp: Date.now() }, ...prevItems];
return newItems.slice(0, 100).sort((a, b) => b.timestamp - a.timestamp);
}
});
setLastContent(content);
}
}, 1000);
return () => clearInterval(interval);
}, [lastContent]);
const handleCopy = async (content: string) => {
await writeText(content);
};
const handleDelete = (timestamp: number) => {
setClipboardItems((prevItems) =>
prevItems.filter((item) => item.timestamp !== timestamp)
);
};
return (
<div>
<h2>クリップボードページ</h2>
<ul>
{clipboardItems.map((item, index) => (
<li key={index}>
<small>{new Date(item.timestamp).toLocaleString()}</small>
<p>{item.content}</p>
<a href="#" onClick={() => handleCopy(item.content)}>コピー</a> |
<a href="#" onClick={() => handleDelete(item.timestamp)}>削除</a>
</li>
))}
</ul>
</div>
);
}
export default ClipBoard;
この実装では、更新されたタイムスタンプ後にリストをソートします。新しい内容が追加されると、リストは時間で再度ソートされます。
ソート後、ページも新しい順序で再表示されるようにします#
Note
ソート後、ページが再表示されないため、強制的に再表示するように要求されました。
GitHub Copilot: ソート後にページが新しい順序で再表示されるようにするために、setClipboardItems
内でリストをソートし、レンダリング時にソートされたリストを使用する必要があります。
以下は更新されたClipBoard.tsx
ファイルです:
// Features/ClipBoard.tsx
import React, { useState, useEffect } from "react";
import { readText, writeText } from "@tauri-apps/plugin-clipboard-manager";
import CryptoJS from "crypto-js";
interface ClipboardItem {
content: string;
md5: string;
timestamp: number;
}
function ClipBoard() {
const [clipboardItems, setClipboardItems] = useState<ClipboardItem[]>([]);
const [lastContent, setLastContent] = useState<string>("");
useEffect(() => {
const interval = setInterval(async () => {
const content = await readText();
if (content && content !== lastContent) {
const md5 = CryptoJS.MD5(content).toString();
setClipboardItems((prevItems) => {
const existingItem = prevItems.find(item => item.md5 === md5);
if (existingItem) {
const updatedItems = prevItems.map(item =>
item.md5 === md5 ? { ...item, timestamp: Date.now() } : item
);
return updatedItems.sort((a, b) => b.timestamp - a.timestamp);
} else {
const newItems = [{ content, md5, timestamp: Date.now() }, ...prevItems];
return newItems.slice(0, 100).sort((a, b) => b.timestamp - a.timestamp);
}
});
setLastContent(content);
}
}, 1000);
return () => clearInterval(interval);
}, [lastContent]);
const handleCopy = async (content: string) => {
await writeText(content);
};
const handleDelete = (timestamp: number) => {
setClipboardItems((prevItems) =>
prevItems.filter((item) => item.timestamp !== timestamp)
);
};
return (
<div>
<h2>クリップボードページ</h2>
<ul>
{clipboardItems.map((item, index) => (
<li key={index}>
<small>{new Date(item.timestamp).toLocaleString()}</small>
<p>{item.content}</p>
<a href="#" onClick={() => handleCopy(item.content)}>コピー</a> |
<a href="#" onClick={() => handleDelete(item.timestamp)}>削除</a>
</li>
))}
</ul>
</div>
);
}
export default ClipBoard;
この実装では、setClipboardItems
内でリストをソートし、レンダリング時にソートされたclipboardItems
リストを使用します。これにより、リストが再度ソートされた後、ページが新しい順序で再表示されます。
隣接する 2 行を異なる背景色で区別します#
Note
UI の最適化。
GitHub Copilot: 隣接する 2 行を異なる背景色で区別するために、CSS スタイルを使用してストライプ効果を実現します。以下は更新されたClipBoard.tsx
ファイルと対応する CSS スタイルです:
まず、ClipBoard.tsx
ファイルを更新します:
// Features/ClipBoard.tsx
import React, { useState, useEffect } from "react";
import { readText, writeText } from "@tauri-apps/plugin-clipboard-manager";
import CryptoJS from "crypto-js";
import "./ClipBoard.css"; // CSSファイルをインポート
interface ClipboardItem {
content: string;
md5: string;
timestamp: number;
}
function ClipBoard() {
const [clipboardItems, setClipboardItems] = useState<ClipboardItem[]>([]);
const [lastContent, setLastContent] = useState<string>("");
useEffect(() => {
const interval = setInterval(async () => {
const content = await readText();
if (content && content !== lastContent) {
const md5 = CryptoJS.MD5(content).toString();
setClipboardItems((prevItems) => {
const existingItem = prevItems.find(item => item.md5 === md5);
if (existingItem) {
const updatedItems = prevItems.map(item =>
item.md5 === md5 ? { ...item, timestamp: Date.now() } : item
);
return updatedItems.sort((a, b) => b.timestamp - a.timestamp);
} else {
const newItems = [{ content, md5, timestamp: Date.now() }, ...prevItems];
return newItems.slice(0, 100).sort((a, b) => b.timestamp - a.timestamp);
}
});
setLastContent(content);
}
}, 1000);
return () => clearInterval(interval);
}, [lastContent]);
const handleCopy = async (content: string) => {
await writeText(content);
};
const handleDelete = (timestamp: number) => {
setClipboardItems((prevItems) =>
prevItems.filter((item) => item.timestamp !== timestamp)
);
};
return (
<div>
<h2>クリップボードページ</h2>
<ul>
{clipboardItems.map((item, index) => (
<li key={index} className={index % 2 === 0 ? "even" : "odd"}>
<small>{new Date(item.timestamp).toLocaleString()}</small>
<p>{item.content}</p>
<a href="#" onClick={() => handleCopy(item.content)}>コピー</a> |
<a href="#" onClick={() => handleDelete(item.timestamp)}>削除</a>
</li>
))}
</ul>
</div>
);
}
export default ClipBoard;
次に、ClipBoard.css
ファイルを作成し、以下のスタイルを追加します:
/* ClipBoard.css */
ul {
list-style-type: none;
padding: 0;
}
li.even {
background-color: #f9f9f9;
}
li.odd {
background-color: #e9e9e9;
}
li {
padding: 10px;
margin: 5px 0;
}
a {
margin-right: 10px;
}
この実装では、以下のことを行います:
ClipBoard.tsx
ファイルでClipBoard.css
ファイルをインポートします。- 隣接する 2 行に異なる CSS クラス(
even
とodd
)を追加して、異なる背景色を適用します。 ClipBoard.css
ファイルでeven
とodd
クラスの背景色を定義します。
これにより、隣接する 2 行が異なる背景色で区別されます。
隣接する 2 行ではなく、隣接する 2 つのクリップボード内容を異なる色で区別します。テキスト内容、時間、操作ボタンを含みます#
GitHub Copilot: 隣接する 2 つのクリップボード内容を異なる色で区別するために、CSS スタイルを使用してストライプ効果を実現します。以下は更新されたClipBoard.tsx
ファイルと対応する CSS スタイルです:
まず、ClipBoard.tsx
ファイルを更新します:
// Features/ClipBoard.tsx
import React, { useState, useEffect } from "react";
import { readText, writeText } from "@tauri-apps/plugin-clipboard-manager";
import CryptoJS from "crypto-js";
import "./ClipBoard.css"; // CSSファイルをインポート
interface ClipboardItem {
content: string;
md5: string;
timestamp: number;
}
function ClipBoard() {
const [clipboardItems, setClipboardItems] = useState<ClipboardItem[]>([]);
const [lastContent, setLastContent] = useState<string>("");
useEffect(() => {
const interval = setInterval(async () => {
const content = await readText();
if (content && content !== lastContent) {
const md5 = CryptoJS.MD5(content).toString();
setClipboardItems((prevItems) => {
const existingItem = prevItems.find(item => item.md5 === md5);
if (existingItem) {
const updatedItems = prevItems.map(item =>
item.md5 === md5 ? { ...item, timestamp: Date.now() } : item
);
return updatedItems.sort((a, b) => b.timestamp - a.timestamp);
} else {
const newItems = [{ content, md5, timestamp: Date.now() }, ...prevItems];
return newItems.slice(0, 100).sort((a, b) => b.timestamp - a.timestamp);
}
});
setLastContent(content);
}
}, 1000);
return () => clearInterval(interval);
}, [lastContent]);
const handleCopy = async (content: string) => {
await writeText(content);
};
const handleDelete = (timestamp: number) => {
setClipboardItems((prevItems) =>
prevItems.filter((item) => item.timestamp !== timestamp)
);
};
return (
<div>
<h2>クリップボードページ</h2>
<ul>
{clipboardItems.map((item, index) => (
<li key={index} className={index % 2 === 0 ? "even" : "odd"}>
<small>{new Date(item.timestamp).toLocaleString()}</small>
<p>{item.content}</p>
<div>
<a href="#" onClick={() => handleCopy(item.content)}>コピー</a> |
<a href="#" onClick={() => handleDelete(item.timestamp)}>削除</a>
</div>
</li>
))}
</ul>
</div>
);
}
export default ClipBoard;
次に、ClipBoard.css
ファイルを作成または更新し、以下のスタイルを追加します:
/* ClipBoard.css */
ul {
list-style-type: none;
padding: 0;
}
li.even {
background-color: #f9f9f9;
}
li.odd {
background-color: #e9e9e9;
}
li {
padding: 10px;
margin: 5px 0;
}
a {
margin-right: 10px;
}
この実装では、以下のことを行います:
ClipBoard.tsx
ファイルでClipBoard.css
ファイルをインポートします。- 隣接する 2 つのクリップボード内容に異なる CSS クラス(
even
とodd
)を追加して、異なる背景色を適用します。 ClipBoard.css
ファイルでeven
とodd
クラスの背景色を定義します。
これにより、隣接する 2 つのクリップボード内容が異なる背景色で区別され、テキスト内容、時間、操作ボタンが含まれます。
内容の背景色調を暗色調に調整し、テキストを対比度の高い色に調整します#
GitHub Copilot: 内容の背景色調を暗色調に調整し、テキストを対比度の高い色に調整するために、ClipBoard.css
ファイルのスタイルを更新します。以下は更新されたClipBoard.css
ファイルです:
/* ClipBoard.css */
ul {
list-style-type: none;
padding: 0;
}
li.even {
background-color: #333; /* 暗色調背景 */
color: #fff; /* 高対比度テキストカラー */
}
li.odd {
background-color: #444; /* 暗色調背景 */
color: #fff; /* 高対比度テキストカラー */
}
li {
padding: 10px;
margin: 5px 0;
}
a {
margin-right: 10px; /* 高対比度リンクカラー */
color: #1e90ff; /* 高対比度リンクカラー */
}
a:hover {
text-decoration: underline;
}
この実装では、以下のことを行います:
li.even
とli.odd
の背景色をそれぞれ#333
と#444
に設定し、暗色調の背景を実現します。li.even
とli.odd
のテキスト色を#fff
に設定し、高対比度のテキスト色を実現します。- リンクの色を
#1e90ff
に設定し、高対比度のリンク色を実現し、ホバー時に下線を追加します。
これにより、内容の背景色調が暗色調に調整され、テキストが対比度の高い色に調整されます。
時間と操作ボタンを 1 行にまとめます#
GitHub Copilot: 時間と操作ボタンを 1 行にまとめるために、CSS Flexbox レイアウトを使用します。以下は更新されたClipBoard.tsx
ファイルと対応する CSS スタイルです:
まず、ClipBoard.tsx
ファイルを更新します:
// Features/ClipBoard.tsx
import React, { useState, useEffect } from "react";
import { readText, writeText } from "@tauri-apps/plugin-clipboard-manager";
import CryptoJS from "crypto-js";
import "./ClipBoard.css"; // CSSファイルをインポート
interface ClipboardItem {
content: string;
md5: string;
timestamp: number;
}
function ClipBoard() {
const [clipboardItems, setClipboardItems] = useState<ClipboardItem[]>([]);
const [lastContent, setLastContent] = useState<string>("");
useEffect(() => {
const interval = setInterval(async () => {
const content = await readText();
if (content && content !== lastContent) {
const md5 = CryptoJS.MD5(content).toString();
setClipboardItems((prevItems) => {
const existingItem = prevItems.find(item => item.md5 === md5);
if (existingItem) {
const updatedItems = prevItems.map(item =>
item.md5 === md5 ? { ...item, timestamp: Date.now() } : item
);
return updatedItems.sort((a, b) => b.timestamp - a.timestamp);
} else {
const newItems = [{ content, md5, timestamp: Date.now() }, ...prevItems];
return newItems.slice(0, 100).sort((a, b) => b.timestamp - a.timestamp);
}
});
setLastContent(content);
}
}, 1000);
return () => clearInterval(interval);
}, [lastContent]);
const handleCopy = async (content: string) => {
await writeText(content);
};
const handleDelete = (timestamp: number) => {
setClipboardItems((prevItems) =>
prevItems.filter((item) => item.timestamp !== timestamp)
);
};
return (
<div>
<h2>クリップボードページ</h2>
<ul>
{clipboardItems.map((item, index) => (
<li key={index} className={index % 2 === 0 ? "even" : "odd"}>
<div className="item-header">
<small>{new Date(item.timestamp).toLocaleString()}</small>
<div>
<a href="#" onClick={() => handleCopy(item.content)}>コピー</a> |
<a href="#" onClick={() => handleDelete(item.timestamp)}>削除</a>
</div>
</div>
<p>{item.content}</p>
</li>
))}
</ul>
</div>
);
}
export default ClipBoard;
次に、ClipBoard.css
ファイルを更新し、以下のスタイルを追加します:
/* ClipBoard.css */
ul {
list-style-type: none;
padding: 0;
}
li.even {
background-color: #333; /* 暗色調背景 */
color: #fff; /* 高対比度テキストカラー */
}
li.odd {
background-color: #444; /* 暗色調背景 */
color: #fff; /* 高対比度テキストカラー */
}
li {
padding: 10px;
margin: 5px 0;
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
}
a {
margin-right: 10px; /* 高対比度リンクカラー */
color: #1e90ff; /* 高対比度リンクカラー */
}
a:hover {
text-decoration: underline;
}
この実装では、以下のことを行います:
ClipBoard.tsx
ファイルで時間と操作ボタンを 1 つのdiv
コンテナに配置し、item-header
クラスを追加します。ClipBoard.css
ファイルでitem-header
クラスを定義し、Flexbox レイアウトを使用して時間と操作ボタンを 1 行にまとめ、両端に配置します。
これにより、時間と操作ボタンが 1 行にまとめられ、内容の背景色調が暗色調に調整され、テキストが対比度の高い色に調整されます。
1 分間隔で現在の内容リストと時間情報を appdata 下の history.json ファイルに保存します#
Note
履歴を永続的に保存します。ここでの読み書き API も同様に v1 の形式で出力されているため、手動で v2 の API 呼び出し方式に調整する必要があります。VSCode の tauri プラグインを使用すると、Ctrl + クリックで関数名を開いて API のソースコードを表示でき、その注釈に呼び出しの参考が示されています。
GitHub Copilot: 1 分間隔で現在の内容リストと時間情報をappdata
下のhistory.json
ファイルに保存するために、以下の手順が必要です:
fs
モジュールを使用してファイルに書き込みます。setInterval
を使用して内容リストを定期的に保存します。- コンポーネントがアンマウントされるときにタイマーをクリアします。
以下は更新されたClipBoard.tsx
ファイルです:
// Features/ClipBoard.tsx
import React, { useState, useEffect } from "react";
import { readText, writeText } from "@tauri-apps/plugin-clipboard-manager";
import { writeTextFile, BaseDirectory } from "@tauri-apps/api/fs";
import CryptoJS from "crypto-js";
import "./ClipBoard.css"; // CSSファイルをインポート
interface ClipboardItem {
content: string;
md5: string;
timestamp: number;
}
function ClipBoard() {
const [clipboardItems, setClipboardItems] = useState<ClipboardItem[]>([]);
const [lastContent, setLastContent] = useState<string>("");
useEffect(() => {
const loadHistory = async () => {
try {
const data = await readTextFile("history.json", { dir: BaseDirectory.AppData });
const items: ClipboardItem[] = JSON.parse(data);
setClipboardItems(items);
} catch (error) {
console.error("履歴の読み込みに失敗しました:", error);
}
};
loadHistory();
}, []);
useEffect(() => {
const interval = setInterval(async () => {
const content = await readText();
if (content && content !== lastContent) {
const md5 = CryptoJS.MD5(content).toString();
setClipboardItems((prevItems) => {
const existingItem = prevItems.find(item => item.md5 === md5);
if (existingItem) {
const updatedItems = prevItems.map(item =>
item.md5 === md5 ? { ...item, timestamp: Date.now() } : item
);
return updatedItems.sort((a, b) => b.timestamp - a.timestamp);
} else {
const newItems = [{ content, md5, timestamp: Date.now() }, ...prevItems];
return newItems.slice(0, 100).sort((a, b) => b.timestamp - a.timestamp);
}
});
setLastContent(content);
}
}, 1000);
return () => clearInterval(interval);
}, [lastContent]);
useEffect(() => {
const saveInterval = setInterval(async () => {
const data = JSON.stringify(clipboardItems, null, 2);
await writeTextFile({ path: "history.json", contents: data }, { dir: BaseDirectory.AppData });
}, 60000); // 1分間隔
return () => clearInterval(saveInterval);
}, [clipboardItems]);
const handleCopy = async (content: string) => {
await writeText(content);
};
const handleDelete = (timestamp: number) => {
setClipboardItems((prevItems) =>
prevItems.filter((item) => item.timestamp !== timestamp)
);
};
return (
<div>
<h2>クリップボードページ</h2>
<ul>
{clipboardItems.map((item, index) => (
<li key={