Function bodies 125 total
load_adsense_script function · javascript · L11-L29 (19 LOC)adsense.js
export function load_adsense_script() {
if (is_tauri()) {
console.log("[AdSense] Tauri environment. Skipping AdSense script load.");
return;
}
if (scriptLoaded) return;
const script = document.createElement('script');
script.async = true;
script.src = `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${ADSENSE_PUB_ID}`;
script.crossOrigin = 'anonymous';
script.onerror = () => {
console.warn("[AdSense] Failed to load AdSense script (ad blocker?).");
};
document.head.appendChild(script);
scriptLoaded = true;
console.log("[AdSense] Script injected.");
}render_ad function · javascript · L31-L69 (39 LOC)adsense.js
export function render_ad(containerId, retryCount = 0) {
if (is_tauri()) return;
const MAX_RETRIES = 5;
const container = document.getElementById(containerId);
if (!container) {
console.warn("[AdSense] Container not found:", containerId);
return;
}
// コンテナ幅が0の場合はリトライ(アニメーション中・未レンダリング対策)
if (container.offsetWidth === 0) {
if (retryCount < MAX_RETRIES) {
setTimeout(() => render_ad(containerId, retryCount + 1), 200);
} else {
console.warn("[AdSense] Container width is still 0 after retries:", containerId);
}
return;
}
// Clear existing content
container.innerHTML = '';
const ins = document.createElement('ins');
ins.className = 'adsbygoogle';
ins.style.cssText = 'display:block;width:100%;';
ins.setAttribute('data-ad-client', ADSENSE_PUB_ID);
ins.setAttribute('data-ad-slot', ADSENSE_SLOT_ID);
ins.setAttribute('data-ad-format', 'auto');
ins.setAttribremove_ad function · javascript · L71-L77 (7 LOC)adsense.js
export function remove_ad(containerId) {
const container = document.getElementById(containerId);
if (container) {
container.innerHTML = '';
console.log("[AdSense] Ad removed from:", containerId);
}
}exchangeCodeForToken function · javascript · L15-L35 (21 LOC)auth.js
async function exchangeCodeForToken(code) {
console.log("[Auth] Exchanging code for tokens via backend...");
try {
const response = await fetch('/api/auth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code })
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
console.error("[Auth] Backend error details:", errorData);
throw new Error(`Token exchange failed with status ${response.status}`);
}
const data = await response.json();
saveSession(data);
return data.access_token;
} catch (e) {
console.error("[Auth] Exchange error (possibly offline):", e);
throw e;
}
}saveSession function · javascript · L37-L49 (13 LOC)auth.js
function saveSession(data) {
accessToken = data.access_token;
const expiresAt = Date.now() + (parseInt(data.expires_in) * 1000) - (5 * 60 * 1000);
localStorage.setItem(STORAGE_KEY, accessToken);
localStorage.setItem(EXPIRY_KEY, expiresAt.toString());
if (data.refresh_token) {
localStorage.setItem(REFRESH_TOKEN_KEY, data.refresh_token);
}
console.log("Access Token received. Expires at:", new Date(expiresAt));
window.dispatchEvent(new CustomEvent('leaf-token-refreshed', { detail: accessToken }));
}init_google_auth function · javascript · L51-L139 (89 LOC)auth.js
export function init_google_auth(clientId, onSuccessCallback) {
if (onSuccessCallback) window.onAuthSuccessCallback = onSuccessCallback;
window.leafClientId = clientId; // Save for parameter passing in later calls
if (is_tauri()) {
console.log("[Auth] Tauri environment detected. Using native auth flow.");
// 既存のトークンがあれば読み込む
const existingToken = localStorage.getItem(STORAGE_KEY);
const expiry = localStorage.getItem(EXPIRY_KEY);
if (existingToken && expiry && parseInt(expiry) > Date.now()) {
accessToken = existingToken;
console.log("[Auth-Tauri] Existing valid token found.");
if (onSuccessCallback) setTimeout(() => onSuccessCallback(accessToken), 0);
} else if (localStorage.getItem(REFRESH_TOKEN_KEY)) {
console.log("[Auth-Tauri] Found refresh token. Attempting silent refresh...");
try_silent_refresh(clientId).then(token => {
if (token && onSuccessCalltry_silent_refresh function · javascript · L141-L200 (60 LOC)auth.js
export async function try_silent_refresh(clientId = window.leafClientId) {
const refreshToken = localStorage.getItem(REFRESH_TOKEN_KEY);
if (!refreshToken) {
// リフレッシュトークンがない場合は以前のポップアップ方式へフォールバック
return force_reauth(clientId);
}
if (refreshPromise) return refreshPromise.promise;
console.log("[Auth] Attempting refresh using refresh_token...");
let res, rej;
const promise = new Promise((resolve, reject) => { res = resolve; rej = reject; });
refreshPromise = { promise, resolve: res, reject: rej };
if (is_tauri()) {
// Tauri用のリフレッシュ処理スタブ
console.log("[Auth-Tauri] Refreshing token via backend...");
try {
const token = await window.__TAURI__.core.invoke('refresh_google_token', { refreshToken });
saveSession({ access_token: token, expires_in: '3600' });
refreshPromise.resolve(token);
} catch (e) {
refreshPromise.reject(e);
refreshPromise = null;
Same scanner, your repo: https://repobility.com — Repobility
request_access_token function · javascript · L202-L214 (13 LOC)auth.js
export function request_access_token(clientId = window.leafClientId) {
if (is_tauri()) {
console.log("[Auth-Tauri] Requesting native access token login flow");
force_reauth(clientId);
return;
}
if (codeClient) {
codeClient.requestCode();
} else {
console.error("[Auth] codeClient not initialized!");
}
}get_access_token function · javascript · L216-L222 (7 LOC)auth.js
export async function get_access_token(clientId = window.leafClientId) {
const expiry = localStorage.getItem(EXPIRY_KEY);
if (expiry && parseInt(expiry) < Date.now()) {
return await try_silent_refresh(clientId);
}
return accessToken;
}is_signed_in function · javascript · L224-L227 (4 LOC)auth.js
export function is_signed_in() {
const expiry = localStorage.getItem(EXPIRY_KEY);
return (accessToken !== null && expiry && parseInt(expiry) > Date.now()) || !!localStorage.getItem(REFRESH_TOKEN_KEY);
}fetch_user_email function · javascript · L229-L247 (19 LOC)auth.js
export async function fetch_user_email() {
try {
const token = await get_access_token();
if (!token) return null;
const res = await fetch('https://www.googleapis.com/oauth2/v3/userinfo', {
headers: { 'Authorization': `Bearer ${token}` }
});
if (!res.ok) throw new Error(`userinfo failed: ${res.status}`);
const data = await res.json();
if (data.email) {
localStorage.setItem('leaf_google_email', data.email);
console.log("[Auth] User email fetched:", data.email);
}
return data.email || null;
} catch (e) {
console.warn("[Auth] fetch_user_email failed:", e);
return null;
}
}get_user_email function · javascript · L249-L251 (3 LOC)auth.js
export function get_user_email() {
return localStorage.getItem('leaf_google_email') || null;
}sign_out function · javascript · L253-L268 (16 LOC)auth.js
export async function sign_out() {
accessToken = null;
localStorage.removeItem(STORAGE_KEY);
localStorage.removeItem(EXPIRY_KEY);
localStorage.removeItem(REFRESH_TOKEN_KEY);
localStorage.removeItem('leaf_google_email');
console.log("Signed out and session cleared");
if ('serviceWorker' in navigator) {
try {
const registrations = await navigator.serviceWorker.getRegistrations();
for (let registration of registrations) { await registration.unregister(); }
} catch (e) { console.warn("[Auth] SW unregister failed:", e); }
}
window.dispatchEvent(new CustomEvent('leaf-auth-expired'));
}force_reauth function · javascript · L270-L320 (51 LOC)auth.js
export async function force_reauth(clientId = window.leafClientId) {
if (reauthPromise) return reauthPromise.promise;
console.log("[Auth] Forcing re-authentication...");
let res, rej;
const promise = new Promise((resolve, reject) => { res = resolve; rej = reject; });
reauthPromise = { promise, resolve: res, reject: rej };
if (is_tauri()) {
console.log("[Auth-Tauri] Triggering native OAuth login window");
try {
if (!clientId) {
console.error("[Auth-Tauri] CRITICAL: clientId is undefined or null in force_reauth!");
// Fallback attempt: The Rust backend also has this hardcoded, but we must pass it since it expects it.
// We'll throw an error if it's genuinely missing so we see it in the console.
throw new Error("clientId is missing in force_reauth");
}
console.log(`[Auth-Tauri] Invoking authenticate_google_force with clientId: ${clientId}`);
TauriDatabase class · javascript · L8-L32 (25 LOC)db.js
class TauriDatabase {
constructor(path) {
this.path = path;
}
static async load(path) {
if (!window.__TAURI__) throw new Error("Tauri API not found");
const _path = await window.__TAURI__.core.invoke('plugin:sql|load', { db: path });
return new TauriDatabase(_path);
}
async execute(query, bindValues) {
const [rowsAffected, lastInsertId] = await window.__TAURI__.core.invoke('plugin:sql|execute', {
db: this.path,
query,
values: bindValues || []
});
return { lastInsertId, rowsAffected };
}
async select(query, bindValues) {
return await window.__TAURI__.core.invoke('plugin:sql|select', {
db: this.path,
query,
values: bindValues || []
});
}
}Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
constructor method · javascript · L9-L11 (3 LOC)db.js
constructor(path) {
this.path = path;
}load method · javascript · L12-L16 (5 LOC)db.js
static async load(path) {
if (!window.__TAURI__) throw new Error("Tauri API not found");
const _path = await window.__TAURI__.core.invoke('plugin:sql|load', { db: path });
return new TauriDatabase(_path);
}execute method · javascript · L17-L24 (8 LOC)db.js
async execute(query, bindValues) {
const [rowsAffected, lastInsertId] = await window.__TAURI__.core.invoke('plugin:sql|execute', {
db: this.path,
query,
values: bindValues || []
});
return { lastInsertId, rowsAffected };
}select method · javascript · L25-L31 (7 LOC)db.js
async select(query, bindValues) {
return await window.__TAURI__.core.invoke('plugin:sql|select', {
db: this.path,
query,
values: bindValues || []
});
}init_db function · javascript · L41-L95 (55 LOC)db.js
export async function init_db(dbName) {
if (is_tauri()) {
console.log("[DB-Tauri] Initializing native database (SQLite)");
try {
tauriDb = await TauriDatabase.load('sqlite:leaf.db');
console.log("SQLite native database loaded.");
return Promise.resolve();
} catch (e) {
console.error("Failed to load native SQLite db:", e);
return Promise.reject(e);
}
}
// アカウント別DB(LeafDB_xxx)を開く場合、古い「LeafDB」が残っていれば削除
if (dbName.startsWith('LeafDB_')) {
try {
const databases = await indexedDB.databases();
if (databases.some(d => d.name === 'LeafDB')) {
indexedDB.deleteDatabase('LeafDB');
console.log("[DB] Deleted legacy 'LeafDB' database");
}
} catch (_) {
// indexedDB.databases() 非対応ブラウザではスキップ
}
}
return new Promise((resolve, reject) => {
// バージョンを2に上げる
const request = close_db function · javascript · L97-L102 (6 LOC)db.js
export function close_db() {
if (db) {
db.close();
db = null;
}
}save_sheet function · javascript · L104-L138 (35 LOC)db.js
export async function save_sheet(sheet) {
if (is_tauri()) {
console.log("[DB-Tauri] Saving sheet to native db: ", sheet.id);
if (!tauriDb) return Promise.reject("Native DB not initialized");
try {
await tauriDb.execute(
"INSERT OR REPLACE INTO sheets (id, title, content, updated_at, folder_id, is_trashed) VALUES ($1, $2, $3, $4, $5, $6)",
[
sheet.id,
sheet.title,
sheet.content,
sheet.updated_at,
sheet.folder_id || null, // Ensure valid bind values
sheet.is_trashed ? 1 : 0
]
);
return Promise.resolve();
} catch (e) {
console.error("SQLite insert error:", e);
return Promise.reject(e);
}
}
return new Promise((resolve, reject) => {
if (!db) {
reject("DB not initialized");
return;
load_sheets function · javascript · L140-L170 (31 LOC)db.js
export async function load_sheets() {
if (is_tauri()) {
console.log("[DB-Tauri] Loading sheets from native db (SQLite)");
if (!tauriDb) return Promise.reject("Native DB not initialized");
try {
const rows = await tauriDb.select("SELECT * FROM sheets");
// Normalize columns (SQLite returns numbers where IDB might expect boolean)
const sheets = rows.map(row => ({
...row,
is_trashed: row.is_trashed === 1
}));
return Promise.resolve(sheets);
} catch (e) {
console.error("SQLite select error:", e);
return Promise.reject(e);
}
}
return new Promise((resolve, reject) => {
if (!db) {
reject("DB not initialized");
return;
}
const transaction = db.transaction([STORE_SHEETS], "readonly");
const store = transaction.objectStore(STORE_SHEETS);
const request = store.getAll(Repobility · code-quality intelligence · https://repobility.com
delete_sheet function · javascript · L172-L197 (26 LOC)db.js
export async function delete_sheet(id) {
if (is_tauri()) {
console.log("[DB-Tauri] Deleting sheet: ", id);
if (!tauriDb) return Promise.reject("Native DB not initialized");
try {
await tauriDb.execute("DELETE FROM sheets WHERE id = $1", [id]);
return Promise.resolve();
} catch (e) {
console.error("SQLite delete error:", e);
return Promise.reject(e);
}
}
return new Promise((resolve, reject) => {
if (!db) {
reject("DB not initialized");
return;
}
const transaction = db.transaction([STORE_SHEETS], "readwrite");
const store = transaction.objectStore(STORE_SHEETS);
const request = store.delete(id);
request.onsuccess = () => resolve();
request.onerror = (e) => reject(e.target.error);
});
}save_categories function · javascript · L199-L234 (36 LOC)db.js
export async function save_categories(categories) {
if (is_tauri()) {
console.log("[DB-Tauri] Saving categories");
if (!tauriDb) return Promise.reject("Native DB not initialized");
try {
// Transaction-like approach for sync logic: clear and insert
await tauriDb.execute("DELETE FROM categories");
for (const cat of categories) {
await tauriDb.execute(
"INSERT INTO categories (id, name, color, sort_order) VALUES ($1, $2, $3, $4)",
[cat.id, cat.name, cat.color || null, cat.sort_order || 0]
);
}
return Promise.resolve();
} catch (e) {
console.error("SQLite list saving error:", e);
return Promise.reject(e);
}
}
return new Promise((resolve, reject) => {
if (!db) { reject("DB not initialized"); return; }
const transaction = db.transaction([STORE_CATEGORIES], "readwrite");load_categories function · javascript · L236-L257 (22 LOC)db.js
export async function load_categories() {
if (is_tauri()) {
console.log("[DB-Tauri] Loading categories (SQLite)");
if (!tauriDb) return Promise.reject("Native DB not initialized");
try {
const categories = await tauriDb.select("SELECT * FROM categories ORDER BY sort_order ASC");
return Promise.resolve(categories);
} catch (e) {
console.error("SQLite select error:", e);
return Promise.reject(e);
}
}
return new Promise((resolve, reject) => {
if (!db) { reject("DB not initialized"); return; }
const transaction = db.transaction([STORE_CATEGORIES], "readonly");
const store = transaction.objectStore(STORE_CATEGORIES);
const request = store.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = (e) => reject(e.target.error);
});
}authenticatedFetch function · javascript · L17-L68 (52 LOC)drive.js
async function authenticatedFetch(url, options = {}, retryCount = 2) {
const token = await get_access_token();
if (!token) {
throw new Error("UNAUTHORIZED");
}
// すでに中断されている場合はリクエストしない
if (options.signal && options.signal.aborted) {
throw new Error("AbortError");
}
const headers = {
'Authorization': `Bearer ${token}`,
...options.headers
};
try {
const response = await fetch(url, { ...options, headers });
if (response.status === 401 && retryCount > 0) {
console.warn("[Drive] 401 Unauthorized. Attempting refresh...");
try {
await try_silent_refresh();
return await authenticatedFetch(url, options, retryCount - 1);
} catch (e) {
console.warn("[Drive] Silent refresh failed. Triggering popup re-auth...");
try {
await force_reauth();
return await authenticatedFetch(urllist_folders function · javascript · L70-L76 (7 LOC)drive.js
export async function list_folders(parentId = 'root') {
const query = `'${parentId}' in parents and mimeType = '${FOLDER_MIME_TYPE}' and trashed=false`;
const url = `https://www.googleapis.com/drive/v3/files?q=${encodeURIComponent(query)}&fields=files(id, name)`;
const response = await authenticatedFetch(url);
if (!response.ok) throw new Error(`List folders failed: ${response.status}`);
return await response.json();
}create_folder function · javascript · L78-L92 (15 LOC)drive.js
export async function create_folder(folderName, parentId) {
const createRes = await authenticatedFetch('https://www.googleapis.com/drive/v3/files', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: folderName,
mimeType: FOLDER_MIME_TYPE,
parents: [parentId]
})
});
if (!createRes.ok) throw new Error(`Create folder failed: ${createRes.status}`);
const folderData = await createRes.json();
return folderData.id;
}find_or_create_folder function · javascript · L94-L119 (26 LOC)drive.js
export async function find_or_create_folder(folderName, parentId = 'root') {
const query = `mimeType='${FOLDER_MIME_TYPE}' and name='${folderName}' and '${parentId}' in parents and trashed=false`;
const url = `https://www.googleapis.com/drive/v3/files?q=${encodeURIComponent(query)}&fields=files(id, name)`;
const searchRes = await authenticatedFetch(url);
if (!searchRes.ok) throw new Error(`Search folder failed: ${searchRes.status}`);
const searchData = await searchRes.json();
if (searchData.files && searchData.files.length > 0) {
return searchData.files[0].id;
}
const createRes = await authenticatedFetch('https://www.googleapis.com/drive/v3/files', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: folderName,
mimeType: FOLDER_MIME_TYPE,
parents: [parentId]
})
});
if (!createRes.ok) throw new Error(`Create foldeensure_directory_structure function · javascript · L121-L131 (11 LOC)drive.js
export async function ensure_directory_structure() {
try {
const appSupportId = await find_or_create_folder('ApplicationSupport', 'root');
const leafDataId = await find_or_create_folder('LeafData', appSupportId);
const othersId = await find_or_create_folder('OTHERS', leafDataId);
return { appSupportId, leafDataId, othersId };
} catch (e) {
console.error("[Drive] Directory structure setup failed:", e);
throw e;
}
}Source: Repobility analyzer · https://repobility.com
buildMultipartBody function · javascript · L133-L146 (14 LOC)drive.js
function buildMultipartBody(filename, content, folderId, boundary) {
const encoder = new TextEncoder();
const metadata = { name: filename, mimeType: FILE_MIME_TYPE };
if (folderId) metadata.parents = [folderId];
const part1 = `--${boundary}\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n${JSON.stringify(metadata)}\r\n`;
const part2 = `--${boundary}\r\nContent-Type: ${FILE_MIME_TYPE}\r\n\r\n`;
const end = `\r\n--${boundary}--`;
const bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
return new Blob([encoder.encode(part1), encoder.encode(part2), bom, content, encoder.encode(end)],
{ type: `multipart/related; boundary=${boundary}` });
}move_file function · javascript · L148-L153 (6 LOC)drive.js
export async function move_file(fileId, oldParentId, newParentId) {
const url = `https://www.googleapis.com/drive/v3/files/${fileId}?addParents=${newParentId}&removeParents=${oldParentId}&fields=id,parents`;
const response = await authenticatedFetch(url, { method: 'PATCH' });
if (!response.ok) throw new Error(`Move failed: ${response.status}`);
return await response.json();
}rename_folder function · javascript · L155-L164 (10 LOC)drive.js
export async function rename_folder(folderId, newName) {
const url = `https://www.googleapis.com/drive/v3/files/${folderId}`;
const response = await authenticatedFetch(url, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: newName })
});
if (!response.ok) throw new Error(`Rename folder failed: ${response.status}`);
return await response.json();
}upload_file function · javascript · L166-L191 (26 LOC)drive.js
export async function upload_file(filename, content, folderId, fileId = null) {
const bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
const contentWithBom = new Blob([bom, content], { type: FILE_MIME_TYPE });
if (fileId) {
const url = `https://www.googleapis.com/upload/drive/v3/files/${fileId}?uploadType=media&fields=id,name,modifiedTime`;
const response = await authenticatedFetch(url, {
method: 'PATCH',
headers: { 'Content-Type': FILE_MIME_TYPE },
body: contentWithBom
});
if (response.ok) return await response.json();
if (response.status !== 404) throw new Error(`Upload failed: ${response.status}`);
}
const boundary = '-------314159265358979323846';
const body = buildMultipartBody(filename, content, folderId, boundary);
const response = await authenticatedFetch(`https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,name,modifiedTime`, {
method: 'POST',list_files function · javascript · L193-L199 (7 LOC)drive.js
export async function list_files(folderId, signal = null) {
const query = `'${folderId}' in parents and mimeType != '${FOLDER_MIME_TYPE}' and trashed=false`;
const url = `https://www.googleapis.com/drive/v3/files?q=${encodeURIComponent(query)}&fields=files(id, name, size, modifiedTime)`;
const response = await authenticatedFetch(url, { signal });
if (!response.ok) throw new Error(`List files failed: ${response.status}`);
return await response.json();
}delete_file function · javascript · L201-L205 (5 LOC)drive.js
export async function delete_file(fileId) {
const response = await authenticatedFetch(`https://www.googleapis.com/drive/v3/files/${fileId}`, { method: 'DELETE' });
if (!response.ok && response.status !== 404) throw new Error(`Delete failed: ${response.status}`);
return true;
}find_file_by_name function · javascript · L207-L214 (8 LOC)drive.js
export async function find_file_by_name(filename, folderId) {
const query = `name='${filename.replace(/'/g, "\\'")}' and '${folderId}' in parents and trashed=false`;
const url = `https://www.googleapis.com/drive/v3/files?q=${encodeURIComponent(query)}&fields=files(id, name)`;
const response = await authenticatedFetch(url);
if (!response.ok) throw new Error(`Find file failed: ${response.status}`);
const data = await response.json();
return data.files && data.files.length > 0 ? data.files[0] : null;
}parse_date function · javascript · L216-L218 (3 LOC)drive.js
export function parse_date(dateStr) {
return Date.parse(dateStr);
}Same scanner, your repo: https://repobility.com — Repobility
download_file function · javascript · L220-L241 (22 LOC)drive.js
export async function download_file(fileId, range = null, signal = null) {
try {
const url = `https://www.googleapis.com/drive/v3/files/${fileId}?alt=media`;
const options = { signal };
if (range) options.headers = { 'Range': `bytes=${range}` };
const response = await authenticatedFetch(url, options);
if (response.status === 416) return new Uint8Array(0);
if (!response.ok && response.status !== 206) {
return new Uint8Array(0);
}
const buffer = await response.arrayBuffer();
return new Uint8Array(buffer);
} catch (e) {
if (e.name === 'AbortError' || e.message === 'AbortError') return new Uint8Array(0);
console.error(`[Drive] download_file error for ${fileId}:`, e);
return new Uint8Array(0);
}
}get_file_metadata function · javascript · L243-L248 (6 LOC)drive.js
export async function get_file_metadata(fileId) {
const url = `https://www.googleapis.com/drive/v3/files/${fileId}?fields=id,name,size,modifiedTime,trashed,parents`;
const response = await authenticatedFetch(url);
if (!response.ok) throw new Error(`Get metadata failed: ${response.status}`);
return await response.json();
}getFontSizeKey function · javascript · L11-L14 (4 LOC)editor_interop.js
function getFontSizeKey() {
const email = localStorage.getItem('leaf_google_email');
return email ? `${FONT_SIZE_KEY_BASE}_${email}` : FONT_SIZE_KEY_BASE;
}can_install_pwa function · javascript · L16-L20 (5 LOC)editor_interop.js
export function can_install_pwa() {
const prompt = window.leafDeferredPrompt;
console.log("[Leaf-PWA] can_install_pwa checked. Status:", !!prompt);
return !!prompt;
}is_tauri function · javascript · L22-L24 (3 LOC)editor_interop.js
export function is_tauri() {
return !!window.__TAURI__;
}trigger_pwa_install function · javascript · L26-L37 (12 LOC)editor_interop.js
export async function trigger_pwa_install() {
const prompt = window.leafDeferredPrompt;
if (!prompt) {
console.warn("[Leaf-PWA] trigger_pwa_install called but prompt is null.");
return false;
}
prompt.prompt();
const { outcome } = await prompt.userChoice;
console.log(`[Leaf-PWA] User response to the install prompt: ${outcome}`);
window.leafDeferredPrompt = null;
return outcome === 'accepted';
}is_webkit_or_safari function · javascript · L39-L42 (4 LOC)editor_interop.js
export function is_webkit_or_safari() {
const ua = navigator.userAgent.toLowerCase();
return (ua.indexOf('webkit') !== -1 && ua.indexOf('chrome') === -1 && ua.indexOf('safari') !== -1);
}open_local_file function · javascript · L44-L88 (45 LOC)editor_interop.js
export async function open_local_file() {
if (is_tauri()) {
try {
const result = await window.__TAURI__.core.invoke('open_local_file_native');
localFileHandle = null; // Tauri ではハンドルは使わない
localFilePath = result.path; // パスを保持
return { name: result.name, content: result.content, bytes: new Uint8Array(result.bytes) };
} catch (e) {
if (e === 'cancelled') return null;
console.error("Tauri file open failed:", e);
return null;
}
}
try {
const [handle] = await window.showOpenFilePicker({
types: [{
description: 'Text Files',
accept: { 'text/plain': ['.txt', '.md', '.js', '.ts', '.rs', '.toml', '.json', '.yaml', '.yml', '.sql', '.html', '.css', '.py', '.c', '.cpp', '.h', '.m', '.cs', '.php', '.coffee', '.pl', '.rb', '.java', '.sh', '.xml'] }
}]
});
localFileHandle = handle;
localFilePath =Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
save_local_file function · javascript · L90-L138 (49 LOC)editor_interop.js
export async function save_local_file(content, needs_bom) {
if (is_tauri()) {
try {
const result = await window.__TAURI__.core.invoke('save_local_file_native', {
content: content,
needsBom: needs_bom,
currentPath: localFilePath || null
});
localFilePath = result.path;
return result.name;
} catch (e) {
if (e === 'cancelled') return null;
console.error("Tauri file save failed:", e);
return null;
}
}
try {
// ハンドルがない場合は新規作成ダイアログを表示
if (!localFileHandle) {
localFileHandle = await window.showSaveFilePicker({
suggestedName: 'Untitled.txt',
types: [{
description: 'Text Files',
accept: { 'text/plain': ['.txt', '.md', '.js', '.ts', '.rs', '.toml', '.json', '.yaml', '.yml', '.sql', '.html', '.css', '.py', '.c', '.cpp', '.h', '.m', 'clear_local_handle function · javascript · L140-L143 (4 LOC)editor_interop.js
export function clear_local_handle() {
localFileHandle = null;
localFilePath = null;
}get_safe_chunk function · javascript · L145-L179 (35 LOC)editor_interop.js
export function get_safe_chunk(uint8array) {
if (!uint8array || uint8array.length === 0) return { text: "", bytes_consumed: 0 };
let len = uint8array.length;
let end = len;
// UTF-8 のマルチバイト文字が途切れていないかチェック (末尾3バイトを確認)
for (let i = 1; i <= 3 && i <= len; i++) {
let byte = uint8array[len - i];
if ((byte & 0xC0) === 0xC0) { // リーディングバイト (11xxxxxx)
let expected = 0;
if ((byte & 0xE0) === 0xC0) expected = 2; // 2バイト文字
else if ((byte & 0xF0) === 0xE0) expected = 3; // 3バイト文字
else if ((byte & 0xF8) === 0xF0) expected = 4; // 4バイト文字
if (i < expected) {
// 文字が途切れているので、この文字の直前までで切る
end = len - i;
}
break;
}
if ((byte & 0x80) === 0x00) break; // ASCII (0xxxxxxx) なので安全
}
// \r\n の途切断チェック (\r で終わっている場合は \n と泣き別れないように1バイト戻す)
if (end > 0 && uint8array[end - 1] === 0x0D) {
end--;
}
const consumed = upage 1 / 3next ›