<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>TRPGシナリオ自動生成&共有</title>
<style>
body {
font-family: 'Segoe UI', sans-serif;
background: #f4f4f4;
padding: 20px;
max-width: 800px;
margin: auto;
}
h1, h2 {
text-align: center;
}
select, button, textarea {
width: 100%;
padding: 10px;
margin-top: 10px;
font-size: 1rem;
}
.box {
background: #fff;
padding: 15px;
border-radius: 8px;
margin-top: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.scenario {
background: #f9f9f9;
border-left: 5px solid #4CAF50;
margin-bottom: 10px;
padding: 10px;
border-radius: 5px;
position: relative;
}
.btns {
margin-top: 10px;
display: flex;
gap: 10px;
}
.copy, .save, .delete {
cursor: pointer;
border: none;
padding: 8px 12px;
border-radius: 4px;
}
.copy { background: #2196F3; color: white; }
.save { background: #4CAF50; color: white; }
.delete { background: #f44336; color: white; }
</style>
</head>
<body>
<h1>🎲 TRPGシナリオ自動生成サイト</h1>
<div class="box">
<label>ジャンル選択:</label>
<select id="genre">
<option value="fantasy">ファンタジー</option>
<option value="horror">ホラー</option>
<option value="scifi">SF</option>
</select>
<button onclick="generateScenario()">📜 シナリオを生成する</button>
</div>
<div id="scenarioBox" class="box"></div>
<h2>🗃 保存済みシナリオ</h2>
<div id="savedScenarios" class="box"></div>
<script>
const data = {
fantasy: {
title: ["封印の迷宮", "聖騎士の試練", "魔王復活の予兆", "失われた王国"],
world: ["中世の王国", "古代の神殿", "天空都市", "エルフの森"],
incident: ["魔法の暴走", "伝説の剣の発見", "精霊の異変", "竜の襲撃"],
npc: ["若き魔導士", "忠義の騎士", "隠者の賢者", "裏切り者の巫女"],
goal: ["真実を解明せよ", "封印を強化せよ", "儀式を阻止せよ", "遺跡の謎を解け"]
},
horror: {
title: ["呪われた村", "深夜の廃病院", "封印されたビデオ", "血塗られた儀式"],
world: ["雨の山奥", "閉鎖された病院", "地下の儀式場", "忘れ去られた洋館"],
incident: ["突然の失踪事件", "見えない何かの影", "不気味な手紙", "鏡の中の誰か"],
npc: ["精神を病んだ神父", "警察官の幽霊", "語らぬ老婆", "首のない少女"],
goal: ["脱出せよ", "謎を暴け", "呪いを解け", "儀式を止めろ"]
},
scifi: {
title: ["星間戦争の火種", "機械反乱の夜", "消えた宇宙船", "記憶を失った地球"],
world: ["宇宙コロニー", "月面都市", "異星の遺跡", "サイバーパンク都市"],
incident: ["AIの暴走", "重力異常", "通信断絶", "テレポート事故"],
npc: ["サイボーグ兵士", "記憶を失った博士", "企業スパイ", "異星の子供"],
goal: ["原因を究明せよ", "AIを停止せよ", "人類を救え", "真実を暴け"]
}
};
function generateScenario() {
const genre = document.getElementById("genre").value;
const g = data[genre];
const title = rand(g.title);
const world = rand(g.world);
const incident = rand(g.incident);
const npc = rand(g.npc);
const goal = rand(g.goal);
const scenarioText = `【タイトル】${title}\n【舞台】${world}\n【事件】${incident}\n【NPC】${npc}\n【目的】${goal}`;
const html = `
<div class="scenario">
<pre>${scenarioText}</pre>
<div class="btns">
<button class="copy" onclick="copyText(\`${scenarioText.replace(/`/g, '\\`')}\`)">📋 コピー</button>
<button class="save" onclick="saveScenario(\`${scenarioText.replace(/`/g, '\\`')}\`)">💾 保存</button>
</div>
</div>
`;
document.getElementById("scenarioBox").innerHTML = html;
}
function rand(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
function copyText(text) {
navigator.clipboard.writeText(text).then(() => {
alert("コピーしました!");
});
}
function saveScenario(text) {
let saved = JSON.parse(localStorage.getItem("trpgScenarios") || "[]");
saved.push(text);
localStorage.setItem("trpgScenarios", JSON.stringify(saved));
alert("保存しました!");
showSaved();
}
function deleteScenario(index) {
let saved = JSON.parse(localStorage.getItem("trpgScenarios") || "[]");
saved.splice(index, 1);
localStorage.setItem("trpgScenarios", JSON.stringify(saved));
showSaved();
}
function showSaved() {
const box = document.getElementById("savedScenarios");
const saved = JSON.parse(localStorage.getItem("trpgScenarios") || "[]");
if (saved.length === 0) {
box.innerHTML = "<p>保存されているシナリオはありません。</p>";
return;
}
box.innerHTML = saved.map((s, i) => `
<div class="scenario">
<pre>${s}</pre>
<div class="btns">
<button class="copy" onclick="copyText(\`${s.replace(/`/g, '\\`')}\`)">📋 コピー</button>
<button class="delete" onclick="deleteScenario(${i})">🗑 削除</button>
</div>
</div>
`).join("");
}
// 初回ロード
showSaved();
</script>
</body>
</html>
カテゴリー: programming
MyAOS.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Site</title>
<link rel="stylesheet" href="https://unpkg.com/aos@next/dist/aos.css" />
<link rel="stylesheet" href="style.css">
</head>
<body>
<section>
<h1>記事一覧</h1>
<article>
<img src="forest.png" width="240" height="160" data-aos="my-animation">
<div class="text">
<h2>タイトル</h2>
<p>こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。</p>
</div>
</article>
<article>
<img src="forest.png" width="240" height="160">
<div class="text">
<h2>タイトル</h2>
<p>こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。</p>
</div>
</article>
<article>
<img src="forest.png" width="240" height="160">
<div class="text">
<h2>タイトル</h2>
<p>こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。</p>
</div>
</article>
</section>
<section>
<h1>お客様の声</h1>
<div class="voices">
<section data-aos="fade-up">
<h2>すばらしい</h2>
<p>素晴らしいです。素晴らしいです。</p>
</section>
<section data-aos="fade-up" data-aos-delay="300">
<h2>すばらしい</h2>
<p>素晴らしいです。素晴らしいです。</p>
</section>
<section data-aos="fade-up" data-aos-delay="600">
<h2>すばらしい</h2>
<p>素晴らしいです。素晴らしいです。</p>
</section>
</div>
</section>
<footer id="footer">
(c) dotinstall.com
</footer>
<a href="#" class="to-top" data-aos="fade-up" data-aos-anchor="#footer" data-aos-offset="0">先頭へ</a>
<script src="https://unpkg.com/aos@next/dist/aos.js"></script>
<script>
AOS.init();
</script>
</body>
</html>
style.css
@charset "utf-8";
body {
margin: 0;
}
body>section {
margin: 0 32px;
}
h1 {
text-align: center;
padding: 160px 0;
}
article {
display: flex;
gap: 16px;
align-items: center;
}
article .text {
flex: 1;
}
article h2 {
margin: 0;
font-size: 22px;
}
article+article {
margin-top: 32px;
}
article:nth-child(even) {
flex-direction: row-reverse;
}
.voices {
display: flex;
gap: 16px;
}
.voices>section {
border-radius: 8px;
border: 1px solid #ccc;
padding: 16px 32px;
text-align: center;
}
footer {
margin-top: 160px;
text-align: center;
padding: 80px 0;
background: #eee;
}
.to-top {
position: fixed;
bottom: 16px;
right: 16px;
}
Z
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Z – 次世代ソーシャルネットワーク</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/aframe/1.5.0/aframe.min.js"></script>
<style>
:root{
--primary:#1DA1F2;--background:#fff;--text:#000;--border:#E1E8ED;--card:#F7F9F9;--danger:#E0245E;
}
[data-theme="dark"]{--background:#15202B;--text:#fff;--border:#38444D;--card:#192734}
*{box-sizing:border-box;margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif}
body{background:var(--background);color:var(--text);min-height:100vh;transition:.3s}
.hidden{display:none}
.wrapper{max-width:640px;margin-inline:auto;padding:20px}
.timeline{margin-top:2rem}
.timeline-item{background:var(--card);border-radius:12px;padding:1rem;margin-bottom:1rem;box-shadow:0 2px 6px rgba(0,0,0,.05)}
.timeline-item h3{margin:0 0 .5rem;font-size:1.1rem}
.timeline-item p{margin:0;white-space:pre-wrap;line-height:1.4}
.timeline-item small{display:block;margin-top:.5rem;font-size:.75rem;color:var(--border)}
.auth-box{background:var(--card);border-radius:12px;padding:1.5rem;margin-bottom:2rem}
.auth-box input{padding:.75rem;border:1px solid var(--border);border-radius:8px;width:100%;margin-bottom:.5rem}
.auth-box button{background:var(--primary);color:white;border:none;border-radius:8px;padding:.75rem;margin-top:.5rem;width:100%;cursor:pointer;font-weight:bold}
.profile-edit{background:var(--card);padding:1rem;border-radius:12px;margin-bottom:2rem}
.profile-edit h3{margin-bottom:.75rem}
.profile-edit input{width:100%;margin:.5rem 0;padding:.5rem;border:1px solid var(--border);border-radius:8px}
.follow-btn{background:#ccc;padding:.3rem .8rem;border-radius:8px;border:none;cursor:pointer;font-size:.85rem;margin-top:.5rem}
img.upload-preview{max-width:100px;border-radius:8px;margin-top:.5rem}
</style>
</head>
<body>
<div class="wrapper">
<div id="authBox" class="auth-box">
<h2>ログイン / 登録</h2>
<input type="email" id="email" placeholder="メールアドレス">
<input type="tel" id="phone" placeholder="電話番号">
<input type="password" id="password" placeholder="パスワード">
<input type="text" id="username" placeholder="ユーザー名">
<button onclick="loginOrRegister()">ログイン / 登録</button>
</div>
<div id="mainBox" class="hidden">
<h1 style="font-size:1.5rem;margin-bottom:1rem">Zタイムライン</h1>
<div style="margin-bottom:1rem">ようこそ、<span id="userEmail"></span> さん!</div>
<div class="profile-edit">
<h3>プロフィール編集</h3>
<input type="text" id="editName" placeholder="表示名を編集">
<input type="text" id="editBio" placeholder="自己紹介を編集">
<button onclick="saveProfile()">プロフィール保存</button>
</div>
<form id="timelineForm" style="display:flex;flex-direction:column;gap:.75rem;margin-bottom:2rem">
<input id="timelineTitle" type="text" placeholder="タイトル" required>
<textarea id="timelineContent" placeholder="投稿内容" required style="min-height:100px"></textarea>
<input type="file" id="imageUpload" accept="image/*">
<img id="preview" class="upload-preview hidden">
<button type="submit">タイムラインに投稿</button>
</form>
<section id="timelineList" class="timeline"></section>
<button onclick="logout()">ログアウト</button>
</div>
</div>
<div id="vrScene" class="hidden" style="position:fixed;inset:0;z-index:9999"></div>
<button id="vrBtn" style="position:fixed;bottom:20px;right:20px;width:56px;height:56px;border-radius:50%;background:var(--primary);color:#fff;border:none;font-size:1.3rem;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 12px rgba(0,0,0,.25)" onclick="enterVR()"><i class="fa-brands fa-vr-cardboard"></i></button>
<script>
let timeline = JSON.parse(localStorage.getItem('z_timeline')||'[]');
let feeds = JSON.parse(localStorage.getItem('z_feeds')||'[]');
let currentUser = JSON.parse(localStorage.getItem('z_user')||'null');
const authBox = document.getElementById('authBox');
const mainBox = document.getElementById('mainBox');
const timelineForm = document.getElementById('timelineForm');
const timelineList = document.getElementById('timelineList');
const userEmailSpan = document.getElementById('userEmail');
const previewImg = document.getElementById('preview');
const imageUpload = document.getElementById('imageUpload');
function loginOrRegister(){
const email = document.getElementById('email').value.trim();
const phone = document.getElementById('phone').value.trim();
const pass = document.getElementById('password').value;
const name = document.getElementById('username').value.trim();
if(!email || !pass || !name){ alert('メール、パスワード、ユーザー名を入力してください'); return; }
currentUser = {email, phone, name, bio:"", followers:[], following:[]};
localStorage.setItem('z_user', JSON.stringify(currentUser));
authBox.classList.add('hidden');
mainBox.classList.remove('hidden');
userEmailSpan.textContent = email;
renderTimeline();
}
function logout(){ localStorage.removeItem('z_user'); location.reload(); }
function saveProfile(){
const name = document.getElementById('editName').value;
const bio = document.getElementById('editBio').value;
if(name) currentUser.name = name;
if(bio) currentUser.bio = bio;
localStorage.setItem('z_user', JSON.stringify(currentUser));
alert('プロフィールを保存しました');
}
function renderTimeline(){
if(!timeline.length){ timelineList.innerHTML = '<p style="color:var(--border)">投稿がまだありません</p>'; return; }
timelineList.innerHTML = timeline.map((t, index)=>{
return `<div class="timeline-item">
<h3>${t.title}</h3>
<p>${t.content}</p>
${t.image ? `<img src="${t.image}" style="max-width:100%;margin-top:.5rem;border-radius:8px">` : ''}
<small>${new Date(t.created).toLocaleString()}</small>
<button onclick="followUser('${t.email}')" class="follow-btn">フォロー</button>
<button onclick="deletePost(${index})" style="margin-top:.5rem;padding:.3rem .6rem;border:none;background:#eee;border-radius:6px;font-size:.8rem;cursor:pointer">削除</button>
</div>`;
}).join('');
}
function followUser(email){
if(!currentUser.following.includes(email)){
currentUser.following.push(email);
localStorage.setItem('z_user', JSON.stringify(currentUser));
alert(`${email} をフォローしました`);
}
}
function deletePost(index){
if(confirm('この投稿を削除しますか?')){
timeline.splice(index,1);
localStorage.setItem('z_timeline', JSON.stringify(timeline));
renderTimeline();
}
}
timelineForm.addEventListener('submit',e=>{
e.preventDefault();
const title = document.getElementById('timelineTitle').value.trim();
const content = document.getElementById('timelineContent').value.trim();
const file = imageUpload.files[0];
if(!title || !content) return;
const newPost = {title, content, image:null, created:new Date().toISOString(), email: currentUser.email};
if(file){
const reader = new FileReader();
reader.onload = ()=>{
newPost.image = reader.result;
timeline.unshift(newPost);
localStorage.setItem('z_timeline', JSON.stringify(timeline));
renderTimeline();
};
reader.readAsDataURL(file);
} else {
timeline.unshift(newPost);
localStorage.setItem('z_timeline', JSON.stringify(timeline));
renderTimeline();
}
timelineForm.reset();
previewImg.classList.add('hidden');
});
imageUpload.addEventListener('change',()=>{
const file = imageUpload.files[0];
if(file){
const reader = new FileReader();
reader.onload = ()=>{
previewImg.src = reader.result;
previewImg.classList.remove('hidden');
};
reader.readAsDataURL(file);
}
});
function botAutoPost(){
const phrases = ['こんにちは!', '今日も頑張ろう!', 'Zへようこそ!'];
const msg = phrases[Math.floor(Math.random()*phrases.length)];
timeline.unshift({title:'BOT投稿', content:msg, image:null, created:new Date().toISOString(), email:'bot@z.jp'});
localStorage.setItem('z_timeline', JSON.stringify(timeline));
renderTimeline();
}
setInterval(botAutoPost, 60000);
function fetchFeed(url){
fetch(`https://api.rss2json.com/v1/api.json?rss_url=${encodeURIComponent(url)}`)
.then(res=>res.json())
.then(data=>{
if(!data.items) return;
data.items.slice(0,3).forEach(item=>{
timeline.unshift({title:item.title, content:item.link, image:null, created:new Date().toISOString(), email:data.feed.title});
});
localStorage.setItem('z_timeline', JSON.stringify(timeline));
renderTimeline();
}).catch(e=>console.error('feed error',e));
}
feeds.forEach(fetchFeed);
function enterVR(){
document.getElementById('vrScene').innerHTML = `
<a-scene embedded>
<a-sky color="#ECECEC"></a-sky>
${timeline.slice(0,10).map((p,i)=>`<a-entity text="value:${p.title}: ${p.content.replace(/\n/g,' ')};wrapCount:30" position="0 ${3-i*1.5} -3"></a-entity>`).join('')}
<a-camera position="0 1.6 0"></a-camera>
</a-scene>`;
document.getElementById('vrScene').classList.remove('hidden');
document.getElementById('vrBtn').classList.add('hidden');
}
document.addEventListener('keydown',e=>{
if(e.key==='Escape' && !document.getElementById('vrScene').classList.contains('hidden')){
document.getElementById('vrScene').classList.add('hidden');
document.getElementById('vrBtn').classList.remove('hidden');
document.getElementById('vrScene').innerHTML='';
}
});
if(currentUser){
authBox.classList.add('hidden');
mainBox.classList.remove('hidden');
userEmailSpan.textContent = currentUser.email;
renderTimeline();
}
</script>
</body>
</html>
MusicPlayer.java
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import javazoom.jl.player.Player; // JLayer
/** 超シンプル MP3 プレイヤー */
public class MusicPlayer extends JFrame {
private static final long serialVersionUID = 1L;
private JButton playBtn = new JButton("▶ 再生");
private JButton stopBtn = new JButton("■ 停止");
private JLabel status = new JLabel("ファイルを開いてください");
private File currentFile;
private Player player; // 再生用スレッド
private Thread playThread;
public MusicPlayer() {
super("Java Swing Music Player");
// 画面レイアウト
JPanel ctrl = new JPanel();
ctrl.add(playBtn);
ctrl.add(stopBtn);
add(ctrl, BorderLayout.CENTER);
add(status, BorderLayout.SOUTH);
// メニュー
JMenuBar bar = new JMenuBar();
JMenu f = new JMenu("ファイル");
JMenuItem open = new JMenuItem("開く...");
open.addActionListener(e -> chooseFile());
f.add(open);
bar.add(f);
setJMenuBar(bar);
// ボタン挙動
playBtn.addActionListener(e -> play());
stopBtn.addActionListener(e -> stop());
// ウィンドウ設定
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setSize(320, 120);
setResizable(false);
setLocationRelativeTo(null);
}
/** ファイル選択ダイアログ */
private void chooseFile() {
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new FileNameExtensionFilter("MP3 Audio", "mp3"));
if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
currentFile = fc.getSelectedFile();
status.setText("選択中: " + currentFile.getName());
}
}
/** 再生開始 */
private void play() {
if (currentFile == null) {
JOptionPane.showMessageDialog(this, "まず MP3 を選択してください");
return;
}
stop(); // 既に再生中なら停止
playThread = new Thread(() -> {
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(currentFile))) {
player = new Player(in);
status.setText("再生中: " + currentFile.getName());
player.play(); // ブロッキング
SwingUtilities.invokeLater(() -> status.setText("停止"));
} catch (Exception ex) {
ex.printStackTrace();
SwingUtilities.invokeLater(() -> status.setText("再生失敗"));
}
});
playThread.start();
}
/** 再生停止 */
private void stop() {
if (player != null) {
player.close();
}
if (playThread != null) {
playThread.interrupt();
}
status.setText("停止");
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new MusicPlayer().setVisible(true));
}
}
C:\java> dir jlayer-1.0.1.jar
2025/05/06 92,109 jlayer-1.0.1.jar ← この行が出れば OK
コンパイル
C:\java> javac -encoding UTF-8 -cp “.;jlayer-1.0.1.jar” MusicPlayer.java
小説投稿サイト
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>小説投稿サイト</title>
<style>
body {
font-family: sans-serif;
padding: 20px;
max-width: 800px;
margin: auto;
background: #f2f2f2;
}
h1 {
text-align: center;
}
form {
background: white;
padding: 20px;
border-radius: 10px;
margin-bottom: 30px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
input, textarea {
width: 100%;
margin-bottom: 10px;
padding: 10px;
border-radius: 5px;
border: 1px solid #ccc;
}
button {
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.post {
background: white;
padding: 15px;
border-left: 5px solid #007bff;
margin-bottom: 20px;
border-radius: 5px;
}
.post h2 {
margin: 0 0 10px;
}
.meta {
color: gray;
font-size: 0.9em;
margin-bottom: 10px;
}
.delete-btn {
background-color: #dc3545;
color: white;
border: none;
padding: 5px 10px;
border-radius: 4px;
float: right;
cursor: pointer;
}
</style>
</head>
<body>
<h1>小説投稿サイト</h1>
<form id="novelForm">
<input type="text" id="author" placeholder="著者名" required>
<input type="text" id="title" placeholder="タイトル" required>
<textarea id="content" rows="8" placeholder="本文" required></textarea>
<button type="submit">投稿する</button>
</form>
<div id="postList"></div>
<script>
const form = document.getElementById('novelForm');
const postList = document.getElementById('postList');
let posts = JSON.parse(localStorage.getItem('novels')) || [];
function saveAndRender() {
localStorage.setItem('novels', JSON.stringify(posts));
renderPosts();
}
function renderPosts() {
postList.innerHTML = '';
[...posts].reverse().forEach((post, index) => {
const div = document.createElement('div');
div.className = 'post';
div.innerHTML = `
<button class="delete-btn" onclick="deletePost(${index})">削除</button>
<h2>${post.title}</h2>
<div class="meta">著者: ${post.author} | 投稿日: ${post.date}</div>
<p>${post.content.replace(/\n/g, '<br>')}</p>
`;
postList.appendChild(div);
});
}
form.addEventListener('submit', e => {
e.preventDefault();
const title = document.getElementById('title').value;
const content = document.getElementById('content').value;
const author = document.getElementById('author').value;
const date = new Date().toLocaleString();
posts.push({ title, content, author, date });
form.reset();
saveAndRender();
});
window.deletePost = function(index) {
posts.splice(posts.length - 1 - index, 1); // reverseしてるため
saveAndRender();
}
renderPosts();
</script>
</body>
</html>
MySQL IF CASE
DROP TABLE IF EXISTS posts;
CREATE TABLE posts (
id INT NOT NULL AUTO_INCREMENT,
message VARCHAR(140),
likes INT,
area VARCHAR(20),
PRIMARY KEY (id)
);
INSERT INTO posts (message, likes, area) VALUES
('post-1', 12, 'Tokyo'),
('post-2', 8, 'Fukuoka'),
('post-3', 11, 'Tokyo'),
('post-4', 3, 'Osaka'),
('post-5', 8, 'Tokyo'),
('post-6', 9, 'Osaka'),
('post-7', 4, 'Tokyo'),
('post-8', 10, 'Osaka'),
('post-9', 31, 'Fukuoka');
CSS メディアクエリー
style.css
@charset "utf-8";
.card {
border: 1px solid #ccc;
box-sizing: border-box;
padding: 16px;
max-width: 600px;
margin: 0 auto;
&:hover {
box-shadow: 0 0 8px rgb(0 0 0 / 0.3);
}
h2 {
margin: 0;
border-bottom: 1px solid #ccc;
text-align: center;
font-size: 18px;
padding-bottom: 8px;
}
p {
margin: 16px 0 0;
line-height: 1.6;
}
}
@media (width >=600px) {
.card h2 {
text-align: left;
}
}
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My CSS Nesting</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<section class="card">
<h2>こんにちは</h2>
<p>こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。</p>
</section>
</body>
</html>
企画書:Windows 12
企画書:Windows 12
1. 概要
- プロジェクト名: Windows 12 開発プロジェクト
- 提案者: (あなたのチーム/部署名などを記載)
- 目的: Windows 11 の次世代OSとして、UI・UX・クラウド連携・セキュリティのさらなる強化を図り、ユーザーに安全かつ便利なコンピューティング環境を提供する。
2. 背景・課題
- Windows 11 の普及とユーザーニーズ
- Windows 11 は現行OSとして機能しているが、新しい体験やさらなる改良を望む声がある。
- タッチ操作・音声操作などマルチインターフェースへの最適化が進む中、UI改善と生産性向上の両立が課題。
- クラウド連携の需要増加
- 在宅勤務やリモート学習の定着で、クラウドストレージ、オンラインコラボレーションツールへの需要が急増。
- Microsoft 365 やAzure など、自社クラウドサービスとのさらなる連携が必要。
- セキュリティ強化
- ランサムウェア、標的型攻撃などサイバー脅威が高度化。
- OSレベルでのゼロトラストセキュリティ対策や暗号化技術、マルチ要素認証との連携強化が必須。
- AIアシスタントとの統合
- MicrosoftのAI技術(例: Cortana / Bing Chat / その他LLMなど)をよりOSレベルに深く取り込み、ユーザー体験を向上させるニーズがある。
3. 目的・ゴール
- UI/UX の再定義: 直感的で分かりやすく、アプリやクラウドサービスとの親和性を高めた新UIの提供。
- 生産性向上: 作業効率を支援する機能(ウィンドウの整理、アプリ間連携、自動翻訳など)を強化。
- 高いセキュリティ: OSレベルのセキュリティ機能を強化し、企業や個人が安心して利用できるプラットフォームを実現。
- クラウド連携強化: OneDrive や SharePoint をさらに活用し、ストレージや共同作業をOS標準機能としてシームレスに統合。
- AIアシスタントの活用: Windowsシステム全体を横断してタスクを支援するAI機能を実装し、スマートな作業環境を提供。
4. ターゲットユーザー
- ビジネスユーザー
- リモートワークやハイブリッドワークの運用が多い企業やフリーランス。
- セキュリティや生産性に強いOSを求める法人顧客。
- コンシューマー(一般ユーザー)
- 最新の機能やUI/UXを快適に使いたい個人。
- PC初心者から上級者まで幅広い層。
- 教育機関
- オンライン学習や共同作業プラットフォームを活用する学校・大学。
- セキュリティや遠隔操作管理の機能が求められる。
5. 特徴・機能
- 新デザインUI「Fluent Next」
- ウィンドウ操作、通知センター、スタートメニューの刷新。
- アクセシビリティを強化し、より柔軟でモダンなデザイン。
- クラウド連携の強化
- OSレベルで OneDrive や Teams が組み込まれ、ファイル共有・共同編集がシームレスに。
- ストレージ残容量やファイル更新情報をリアルタイムで通知。
- Windows Copilot(AIアシスタント)の進化
- 音声入力やチャット操作など、各シーンに応じてマルチタスクを支援。
- 自動サマリー作成やメール下書きなどをサポート。
- セキュリティ / ゼロトラスト対応
- TPM 2.0 を活用したハードウェアレベルの暗号化と認証強化。
- フィッシング防止やマルウェア対策をさらに高度化。
- 企業向けにはクラウドポリシーと連携し、認証管理を一元化。
- パフォーマンス最適化 / 省電力
- OSカーネルと電源管理の改善により、省電力モード時のバッテリー持続時間を向上。
- ゲーミングモードやクリエイティブモードなど、状況に応じたリソース割り当てを自動調整。
- 拡張ディスプレイ & マルチデバイス対応
- タブレットや折りたたみ型PCなど、画面サイズが変化するデバイスでの最適化。
- スマホとの連携強化(電話・メッセージ通知、写真自動同期など)。
6. 開発スケジュール(例)
| フェーズ | 期間 | 主なタスク |
|---|---|---|
| 要件定義 | 20XX年 Q1~Q2 | UIデザイン要件決定、主要機能の企画設計 |
| 開発・実装 | 20XX年 Q3~20XY年Q1 | コア機能実装、セキュリティ強化 内部テスト |
| パブリックβ版 | 20XY年 Q2 | パブリックベータテスト 外部フィードバック回収 |
| 正式リリース | 20XY年 Q3 | 最終調整・品質保証 グローバルローンチ |
7. 予算・リソース
- 人件費: エンジニア、UI/UXデザイナー、QA、プロジェクトマネージャーなど。
- インフラ費: クラウドサーバー、CI/CD環境、テスト機材購入など。
- マーケティング費: リリース前後の広告宣伝費、イベント開催費など。
- 合計予算の目安: (現実的には大きな額になりますが、具体的な金額はプロジェクト規模に依存)
8. 成功指標 (KPI)
- アップグレード率: 既存ユーザーからの移行数、Windows 10/11ユーザーのアップデート率。
- エラー/クラッシュレポートの減少率: OSの安定性・信頼性を継続してモニタリング。
- クラウドサービス利用率: OneDrive / Teams などの利用継続率、導入企業数。
- ユーザー満足度: フィードバックやアンケートによるNPS(ネット・プロモーター・スコア)など。
9. リスクと対策
- 既存アプリ互換性
- 過去OS向けアプリが動作しない問題。
- → 互換レイヤーや仮想化技術を提供し、互換性向上に注力。
- 大規模なUI変更に対する反発
- → カスタマイズ性を高め、ユーザーが旧UIレイアウトへ切り替え可能なモードを検討。
- セキュリティ脆弱性
- → リリース前のセキュリティ監査を強化し、定期的に更新プログラムを配信。
10. まとめ
- Windows 12 は 「セキュリティ強化」「UI/UX革新」「AI活用」「クラウド統合」 を核とした次世代OSとなる。
- 企業・教育機関・一般ユーザーなど幅広い層に向けて、安全かつ効率的な作業環境 を提供することをゴールとする。
- 今後は段階的に開発と検証を進め、ユーザーからのフィードバックを取り込みながら 正式リリース を目指す。
Vooglebrowser
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Voogleブラウザ</title>
<meta property="og:title" content="Voogleブラウザ">
<meta property="og:description" content="次世代タブブラウザ Voogle">
<meta property="og:url" content="https://voogle.onrender.com/">
<meta property="og:type" content="website">
<meta property="og:image" content="https://voogle.onrender.com/ogp.png">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<style>
body.dark {
background-color: #222;
color: #fff;
}
header {
background-color: #4285F4;
padding: 15px;
color: white;
font-size: 24px;
text-align: center;
}
#tabs {
display: flex;
background-color: #e0e0e0;
padding: 10px;
overflow-x: auto;
}
.tab {
padding: 8px 16px;
background-color: white;
border-radius: 20px;
margin-right: 10px;
cursor: grab;
font-weight: bold;
position: relative;
user-select: none;
}
.tab.active {
background-color: #34A853;
color: white;
}
.tab .close {
position: absolute;
top: 0;
right: 5px;
cursor: pointer;
font-family: 'Material Icons';
}
#navbar {
display: flex;
padding: 10px;
background-color: #f1f1f1;
gap: 10px;
}
#url {
flex: 1;
padding: 10px;
border-radius: 20px;
border: 1px solid #ccc;
}
button {
padding: 10px 20px;
background-color: #4285F4;
color: white;
border: none;
border-radius: 20px;
cursor: pointer;
display: flex;
align-items: center;
gap: 5px;
}
#spinner {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 24px;
}
iframe {
width: 100%;
height: calc(100% - 240px);
border: none;
}
footer {
padding: 10px;
background-color: #e0e0e0;
text-align: center;
font-size: 12px;
}
</style>
</head>
<body>
<header>
<span class="material-icons" style="vertical-align: middle; margin-right: 10px;">language</span>
<a href="https://voogle.onrender.com/">Voogleブラウザ</a>
</header>
<div id="tabs"></div>
<div id="navbar">
<input type="text" id="url" placeholder="https://example.com">
<button id="go"><span class="material-icons">arrow_forward</span>移動</button>
<button id="newTab"><span class="material-icons">add</span>タブ追加</button>
<button id="darkModeToggle"><span class="material-icons">dark_mode</span>ダークモード</button>
</div>
<div id="spinner">読み込み中...</div>
<iframe id="viewer"></iframe>
<footer>
© 2025 Voogle Inc.
</footer>
<script>
const tabs = document.getElementById('tabs');
const viewer = document.getElementById('viewer');
const urlInput = document.getElementById('url');
const goButton = document.getElementById('go');
const newTabButton = document.getElementById('newTab');
const darkModeToggle = document.getElementById('darkModeToggle');
const spinner = document.getElementById('spinner');
let tabData = [];
let activeTab = -1;
function switchTab(index) {
activeTab = index;
document.querySelectorAll('.tab').forEach((tab, i) => {
tab.classList.toggle('active', i === index);
});
loadPage(tabData[index].url);
}
function addTab(url = 'https://voogle.onrender.com/') {
tabData.push({ url });
const index = tabData.length - 1;
const tab = document.createElement('div');
tab.className = 'tab';
tab.setAttribute('draggable', true);
tab.innerHTML = `タブ ${index + 1} <span class="close">close</span>`;
tab.onclick = () => switchTab(index);
tab.querySelector('.close').onclick = (e) => {
e.stopPropagation();
tabs.removeChild(tab);
tabData.splice(index, 1);
if (activeTab === index) {
viewer.src = '';
urlInput.value = '';
}
};
tabs.appendChild(tab);
switchTab(index);
}
function loadPage(url) {
spinner.style.display = 'block';
viewer.src = url;
urlInput.value = url;
viewer.onload = () => {
spinner.style.display = 'none';
document.title = viewer.contentDocument.title || 'Voogleブラウザ';
};
}
goButton.onclick = () => {
if (activeTab >= 0) {
let url = urlInput.value.trim();
if (!url.startsWith('http')) {
url = 'https://' + url;
}
tabData[activeTab].url = url;
loadPage(url);
}
};
newTabButton.onclick = () => addTab();
darkModeToggle.onclick = () => {
document.body.classList.toggle('dark');
localStorage.setItem('darkMode', document.body.classList.contains('dark'));
};
if (localStorage.getItem('darkMode') === 'true') {
document.body.classList.add('dark');
}
addTab();
</script>
</body>
</html>
消費型オタクと生産型オタクの違い
消費型オタクと生産型オタクの違いは、趣味への関わり方にあります。
消費型オタク
- 主に 作品を楽しむことが中心 のオタク。
- アニメ、漫画、ゲーム、小説などを 視聴・購読・プレイ して楽しむ。
- グッズやフィギュア、Blu-rayなどを 購入 する。
- SNSや掲示板で 感想を共有 する。
- コスプレやイベント参加など、体験型の楽しみ方をすることも。
例
- アニメをリアルタイムで視聴し、円盤やグッズを買う。
- 推しのキャラクターのフィギュアやタペストリーを集める。
- ゲームをプレイして感想をツイートする。
生産型オタク
- 自ら創作活動を行う オタク。
- イラスト、小説、同人誌、漫画、動画、音楽、ゲーム制作などの 二次創作・オリジナル作品を作る。
- 3Dモデリング、プログラミング、AI技術などを活かして創作することも。
- YouTube、Pixiv、ニコニコ動画、BOOTHなどで 作品を発表・販売 する。
例
- 好きなアニメのキャラを描いてSNSに投稿する。
- 同人誌や同人ゲームを作成してイベント(例:コミケ)で頒布する。
- VTuberのLive2Dモデルを作成する。
- AIを使ってオリジナルの物語を生成する。
ハイブリッド型(両方)
最近は、消費しながら創作する ハイブリッド型 のオタクも多いです。
- 最初は消費型→生産型に移行する 例も多い。
- 例:「好きな作品の二次創作イラストを描き始めたら、いつの間にか生産型になっていた」
- 逆に、生産型だけど他の作品も 消費してインスピレーションを得る こともある。
どちらが良い・悪いという話ではない
- 消費型は市場を支える → コンテンツが売れないと創作活動も続かない。
- 生産型は新しいコンテンツを生み出す → 二次創作が原作の人気を支えることも。
- どちらもオタク文化の重要な一部。
結論:「消費型も生産型も、それぞれの楽しみ方がある!」
