Compare commits

..

No commits in common. "1a2e5ff3420ce14bbe45e0ff528a08b5aba9ec9d" and "c825fb3f743474c0a48b830200a20dd44875c454" have entirely different histories.

5 changed files with 4 additions and 274 deletions

View File

@ -29,7 +29,7 @@ VSCode拡張機能「ImageMarkPengent」の実装タスク一覧です。
### 1. コマンド登録と右クリックメニュー対応
- [x] `package.json` にコマンドを登録する
- [x] `explorer/context` メニューに「Open in ImageMarkPengent」を追加
- [x] 対象拡張子を `.png` / `.jpg` / `.jpeg` に限定
- [ ] 対象拡張子を `.png` / `.jpg` / `.jpeg` に限定
### 2. WebViewで画像表示
- [ ] コマンド実行時にWebViewパネルを開く

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

View File

@ -2,7 +2,6 @@
"name": "imagemarkpengent",
"displayName": "ImageMarkPengent",
"description": "",
"license": "MIT",
"version": "0.0.1",
"engines": {
"vscode": "^1.99.0"

View File

@ -1,8 +1,6 @@
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
const path = require('path');
import { getWebviewContent } from './webviewContent';
// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
@ -24,22 +22,12 @@ export function activate(context: vscode.ExtensionContext) {
vscode.window.showWarningMessage('対応画像ファイル(.png, .jpg, .jpegを選択してください。');
return;
}
const panel = vscode.window.createWebviewPanel(
'imageEditor',
'ImageMarkPengent',
vscode.ViewColumn.Two,
{
enableScripts: true,
localResourceRoots: [vscode.Uri.file(path.dirname(uri.fsPath))]
}
);
const imageSrc = panel.webview.asWebviewUri(uri);
panel.webview.html = getWebviewContent(imageSrc.toString());
vscode.window.showInformationMessage('画像編集コマンドが呼び出されました: ' + uri.fsPath);
});
context.subscriptions.push(disposable);
context.subscriptions.push(disposableOpenImageEditor);
}
// This method is called when your extension is deactivated
export function deactivate() {}

View File

@ -1,257 +0,0 @@
export function getWebviewContent(imageSrc: string): string {
return `
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ImageMarkPengent</title>
<style>
html, body { height: 100%; margin: 0; padding: 0; overflow: hidden; }
#toolbar {
position: absolute;
top: 10px;
left: 10px;
background: rgba(255,255,255,0.95);
border-radius: 6px;
box-shadow: 0 2px 8px #0002;
padding: 8px 12px;
z-index: 10;
display: flex;
gap: 8px;
align-items: center;
font-size: 14px;
}
#canvas { display: block; width: 100vw; height: 100vh; background: #222; cursor: grab; }
.active-btn { background: #1976d2; color: #fff; border-radius: 4px; }
</style>
</head>
<body>
<div id="toolbar">
<button id="moveBtn" class="active-btn"></button>
<button id="markBtn">(Ctr+)</button>
<label>:
<select id="lineWidth">
<option value="2">2px</option>
<option value="4">4px</option>
<option value="8">8px</option>
</select>
</label>
<label>:
<input type="color" id="colorPicker" value="#ff0000" />
</label>
</div>
<canvas id="canvas"></canvas>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let img = new Image();
img.src = "${imageSrc}";
// UI要素
const moveBtn = document.getElementById('moveBtn');
const markBtn = document.getElementById('markBtn');
const lineWidthSelect = document.getElementById('lineWidth');
const colorPicker = document.getElementById('colorPicker');
// 状態
let mode = 'move'; // 'move' or 'mark'
let scale = 1;
let minScale = 0.1;
let maxScale = 10;
let offsetX = 0;
let offsetY = 0;
let isDragging = false;
let dragStartX = 0;
let dragStartY = 0;
let lastOffsetX = 0;
let lastOffsetY = 0;
let ctrlPressed = false;
// マーク(楕円)の配列
let marks = [];
// 一時的なマーク(ドラッグ中のプレビュー)
let tempMark = null;
let markStart = null;
// 設定
let markColor = colorPicker.value;
let markLineWidth = parseInt(lineWidthSelect.value, 10);
// UIイベント
moveBtn.onclick = () => setMode('move');
markBtn.onclick = () => setMode('mark');
lineWidthSelect.onchange = () => { markLineWidth = parseInt(lineWidthSelect.value, 10); draw(); };
colorPicker.oninput = () => { markColor = colorPicker.value; draw(); };
function setMode(newMode) {
mode = newMode;
if (mode === 'move') {
moveBtn.classList.add('active-btn');
markBtn.classList.remove('active-btn');
canvas.style.cursor = 'grab';
} else {
moveBtn.classList.remove('active-btn');
markBtn.classList.add('active-btn');
canvas.style.cursor = 'crosshair';
}
// プレビュー消去
tempMark = null;
markStart = null;
draw();
}
// Ctrlキー押下・離上で一時的にマーク追加モード
window.addEventListener('keydown', (e) => {
if (e.key === 'Control') {
ctrlPressed = true;
canvas.style.cursor = 'crosshair';
}
if (e.key === 'Escape') {
setMode('move');
}
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'z') {
undo();
}
});
window.addEventListener('keyup', (e) => {
if (e.key === 'Control') {
ctrlPressed = false;
if (mode === 'move') canvas.style.cursor = 'grab';
}
});
// リサイズ対応
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
draw();
}
window.addEventListener('resize', resizeCanvas);
// 画像ロード後、初期ズーム・位置を計算
img.onload = () => {
const scaleX = canvas.width / img.width;
const scaleY = canvas.height / img.height;
scale = Math.min(scaleX, scaleY, 1);
offsetX = (canvas.width - img.width * scale) / 2;
offsetY = (canvas.height - img.height * scale) / 2;
draw();
};
// 描画関数
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.setTransform(scale, 0, 0, scale, offsetX, offsetY);
ctx.drawImage(img, 0, 0);
// マークをすべて描画
for (const mark of marks) {
drawEllipse(mark.x1, mark.y1, mark.x2, mark.y2, mark.color, mark.lineWidth);
}
// プレビュー中のマーク
if (tempMark) {
drawEllipse(tempMark.x1, tempMark.y1, tempMark.x2, tempMark.y2, tempMark.color, tempMark.lineWidth, true);
}
ctx.restore();
}
function drawEllipse(x1, y1, x2, y2, color, lineWidth, dashed = false) {
ctx.save();
ctx.beginPath();
const cx = (x1 + x2) / 2;
const cy = (y1 + y2) / 2;
const rx = Math.abs(x2 - x1) / 2;
const ry = Math.abs(y2 - y1) / 2;
ctx.ellipse(cx, cy, rx, ry, 0, 0, 2 * Math.PI);
ctx.strokeStyle = color;
ctx.lineWidth = lineWidth / scale;
if (dashed) ctx.setLineDash([6 / scale, 6 / scale]);
ctx.stroke();
ctx.setLineDash([]);
ctx.restore();
}
// ホイールでズーム
canvas.addEventListener('wheel', (e) => {
e.preventDefault();
const mouseX = e.offsetX;
const mouseY = e.offsetY;
const prevScale = scale;
if (e.deltaY < 0) {
scale *= 1.1;
} else {
scale /= 1.1;
}
scale = Math.max(minScale, Math.min(maxScale, scale));
offsetX = mouseX - ((mouseX - offsetX) * (scale / prevScale));
offsetY = mouseY - ((mouseY - offsetY) * (scale / prevScale));
draw();
}, { passive: false });
// --- マーク追加モード or Ctrl+ドラッグで楕円追加 ---
canvas.addEventListener('mousedown', (e) => {
const rect = canvas.getBoundingClientRect();
const x = (e.clientX - rect.left - offsetX) / scale;
const y = (e.clientY - rect.top - offsetY) / scale;
if (mode === 'mark' || ctrlPressed) {
if (x >= 0 && y >= 0 && x <= img.width && y <= img.height) {
markStart = { x1: x, y1: y };
tempMark = { x1: x, y1: y, x2: x, y2: y, color: markColor, lineWidth: markLineWidth };
}
} else if (mode === 'move') {
isDragging = true;
dragStartX = e.clientX;
dragStartY = e.clientY;
lastOffsetX = offsetX;
lastOffsetY = offsetY;
canvas.style.cursor = 'grabbing';
}
});
window.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
const x = (e.clientX - rect.left - offsetX) / scale;
const y = (e.clientY - rect.top - offsetY) / scale;
if ((mode === 'mark' || ctrlPressed) && markStart) {
tempMark = { x1: markStart.x1, y1: markStart.y1, x2: x, y2: y, color: markColor, lineWidth: markLineWidth };
draw();
} else if (mode === 'move' && isDragging) {
offsetX = lastOffsetX + (e.clientX - dragStartX);
offsetY = lastOffsetY + (e.clientY - dragStartY);
draw();
}
});
window.addEventListener('mouseup', (e) => {
if ((mode === 'mark' || ctrlPressed) && markStart && tempMark) {
if (Math.abs(tempMark.x2 - tempMark.x1) >= 2 / scale && Math.abs(tempMark.y2 - tempMark.y1) >= 2 / scale) {
pushUndo();
marks.push({ ...tempMark });
}
markStart = null;
tempMark = null;
if (mode === 'mark') setMode('move');
draw();
} else if (mode === 'move' && isDragging) {
isDragging = false;
canvas.style.cursor = 'grab';
}
});
let undoStack = [];
function pushUndo() {
undoStack.push(JSON.stringify(marks));
if (undoStack.length > 100) undoStack.shift();
}
function undo() {
if (undoStack.length > 0) {
marks = JSON.parse(undoStack.pop());
draw();
}
}
// 初期リサイズ
resizeCanvas();
</script>
</body>
</html>
`;
}