photosuite 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +244 -0
- package/README.zh-CN.md +245 -0
- package/dist/Wild-squirrel-feeding-interaction-with-hand-in-natural-forest.png +0 -0
- package/dist/demo.d.ts +0 -0
- package/dist/dom-CDvVCbvk.js +36 -0
- package/dist/dom-DMZsb49p.cjs +1 -0
- package/dist/exif-DYpA7_Ip.cjs +1 -0
- package/dist/exif-Dr_aoh2F.js +7 -0
- package/dist/glightbox-Cnkb5Vpt.js +44 -0
- package/dist/glightbox-D14wFv8j.cjs +1 -0
- package/dist/imageAlts-BAYP9b5j.js +7 -0
- package/dist/imageAlts-CAX8Uksx.cjs +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/integration.d.ts +18 -0
- package/dist/modules/dom.d.ts +40 -0
- package/dist/modules/exif.d.ts +12 -0
- package/dist/modules/glightbox.d.ts +9 -0
- package/dist/modules/imageAlts.d.ts +12 -0
- package/dist/photosuite.cjs.js +1 -0
- package/dist/photosuite.css +2 -0
- package/dist/photosuite.es.js +21 -0
- package/dist/photosuite.integration.cjs +7 -0
- package/dist/photosuite.integration.js +173 -0
- package/dist/rehype/exiftoolVendored.d.ts +22 -0
- package/dist/remark/imageUrl.d.ts +17 -0
- package/dist/types.d.ts +134 -0
- package/dist/vite.svg +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file exif.ts
|
|
3
|
+
* @description EXIF 模块,负责启用和管理图片的 EXIF 信息显示功能
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 启用 EXIF 显示功能
|
|
7
|
+
*
|
|
8
|
+
* 动态加载 EXIF 样式,并处理页面上的媒体元素以添加 EXIF 显示条
|
|
9
|
+
*
|
|
10
|
+
* @param selector - 图片选择器
|
|
11
|
+
*/
|
|
12
|
+
export declare function enableExif(scope: string, selector: string): Promise<void>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file imageAlts.ts
|
|
3
|
+
* @description 图片标题模块,负责将图片的 alt 属性作为标题显示
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 启用图片标题显示功能
|
|
7
|
+
*
|
|
8
|
+
* 动态加载标题样式,并处理页面上的媒体元素以添加标题
|
|
9
|
+
*
|
|
10
|
+
* @param selector - 图片选择器
|
|
11
|
+
*/
|
|
12
|
+
export declare function enableImageAlts(scope: string, selector: string): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){let t=e.scope;if(!t){console.warn(`Photosuite: 'scope' option is required.`);return}let n=e.selector??`a.glightbox`,r=e.gallery??`markdown`,i=e.glightbox??!0,a=e.imageAlts??!0,o=e.exif??!0,s=async()=>{if(!document.querySelector(t))return;let s=[];i&&s.push(Promise.resolve().then(()=>require(`./glightbox-D14wFv8j.cjs`)).then(i=>i.initGlightboxModule({selector:n,scope:t,gallery:r,options:e.glightboxOptions,cssUrl:e.glightboxCssUrl,jsUrl:e.glightboxJsUrl}))),a&&s.push(Promise.resolve().then(()=>require(`./imageAlts-CAX8Uksx.cjs`)).then(e=>e.enableImageAlts(t,n))),o&&s.push(Promise.resolve().then(()=>require(`./exif-DYpA7_Ip.cjs`)).then(e=>e.enableExif(t,n))),s.length&&Promise.all(s).catch(()=>{})};document.readyState===`complete`||document.readyState===`interactive`?s():document.addEventListener(`DOMContentLoaded`,s)}exports.photosuite=e;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
:root{--photosuite-caption-size:12px;--photosuite-caption-color:#fff;--photosuite-exif-size:12px;--photosuite-exif-color:#fff;--photosuite-exif-bg:linear-gradient(to bottom,#0009,transparent);--photosuite-caption-bg:linear-gradient(to right,#000c,transparent)}:where(.photosuite-item img){display:block;box-shadow:none!important;border:none!important;border-radius:0!important;outline:none!important;margin:0!important;padding:0!important}.photosuite-item{line-height:0;display:inline-block;position:relative;overflow:hidden}.photosuite-item>a{display:block}.photosuite-caption{box-sizing:border-box;z-index:1;background:var(--photosuite-caption-bg);justify-content:flex-start;align-items:center;height:30px;padding:0 10px;font-style:normal;font-weight:400;line-height:1;display:flex;position:absolute;bottom:0;left:0;right:0;font-size:var(--photosuite-caption-size)!important;color:var(--photosuite-caption-color)!important}.photosuite-exif{box-sizing:border-box;z-index:1;background:var(--photosuite-exif-bg);opacity:0;pointer-events:none;justify-content:center;align-items:center;height:30px;padding:0 10px;font-style:normal;font-weight:400;line-height:1;transition:opacity .2s ease-in-out;display:flex;position:absolute;top:0;left:0;right:0;font-size:var(--photosuite-exif-size)!important;color:var(--photosuite-exif-color)!important}.photosuite-item:hover .photosuite-exif{opacity:1}
|
|
2
|
+
/*$vite$:1*/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
function photosuite(e) {
|
|
2
|
+
let t = e.scope;
|
|
3
|
+
if (!t) {
|
|
4
|
+
console.warn("Photosuite: 'scope' option is required.");
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
let n = e.selector ?? "a.glightbox", r = e.gallery ?? "markdown", i = e.glightbox ?? !0, a = e.imageAlts ?? !0, o = e.exif ?? !0, s = async () => {
|
|
8
|
+
if (!document.querySelector(t)) return;
|
|
9
|
+
let s = [];
|
|
10
|
+
i && s.push(import("./glightbox-Cnkb5Vpt.js").then((i) => i.initGlightboxModule({
|
|
11
|
+
selector: n,
|
|
12
|
+
scope: t,
|
|
13
|
+
gallery: r,
|
|
14
|
+
options: e.glightboxOptions,
|
|
15
|
+
cssUrl: e.glightboxCssUrl,
|
|
16
|
+
jsUrl: e.glightboxJsUrl
|
|
17
|
+
}))), a && s.push(import("./imageAlts-BAYP9b5j.js").then((e) => e.enableImageAlts(t, n))), o && s.push(import("./exif-Dr_aoh2F.js").then((e) => e.enableExif(t, n))), s.length && Promise.all(s).catch(() => {});
|
|
18
|
+
};
|
|
19
|
+
document.readyState === "complete" || document.readyState === "interactive" ? s() : document.addEventListener("DOMContentLoaded", s);
|
|
20
|
+
}
|
|
21
|
+
export { photosuite };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Object.defineProperty(exports,`__esModule`,{value:!0});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`exiftool-vendored`),l=require(`node:fs`);l=s(l);let u=require(`node:fs/promises`);u=s(u);let d=require(`node:path`);d=s(d);let f=require(`node:http`);f=s(f);let p=require(`node:https`);p=s(p);let m=require(`node:os`);m=s(m);let h=require(`node:url`);var g=(...e)=>{let t=e.filter(e=>typeof e==`string`&&e!==``);if(t.length===0)return``;if(t.length===1)return t[0];let n=t[0],r=t[1],i=`${n.endsWith(`/`)?n.slice(0,-1):n}/${r.startsWith(`/`)?r.slice(1):r}`;return t.length>2?g(i,...t.slice(2)):i},_=e=>!(!e||/^https?:\/\//i.test(e)||e.startsWith(`/`)||e.startsWith(`./`)||e.startsWith(`../`));function v(e={}){let{imageBase:t,imageDir:n=`imageDir`,fileDir:r=!1}=e;return(e,i)=>{let a=(i?.data?.astro?.frontmatter||i?.data?.frontmatter||{})[n]||``,o=(i?.path||i?.history?.[0]||``).split(/[\\/]/).pop()?.split(`.`).shift()||``,s=r?o:a,c=e=>{if(e){if(e.type===`image`){let n=e.url||``;(t||s)&&_(n)&&(e.url=g(t,s,n))}e.children&&e.children.length&&e.children.forEach(c)}};c(e)}}function y(e){try{let t=new h.URL(e);return t.protocol===`http:`||t.protocol===`https:`}catch{return!1}}async function b(e){let t=m.tmpdir(),n=d.extname(e)||`.bin`,r=d.join(t,`exif-${Date.now()}-${Math.random().toString(36).slice(2)}${n}`),i=l.createWriteStream(r),a=e.startsWith(`https:`)?p:f;return await new Promise((t,n)=>{a.get(e,e=>{if(e.statusCode&&e.statusCode>=300&&e.statusCode<400&&e.headers.location,e.statusCode!==200){n(Error(`HTTP `+e.statusCode));return}e.pipe(i),i.on(`finish`,t),i.on(`error`,n)}).on(`error`,n)}),{path:r,cleanup:async()=>{try{await u.unlink(r)}catch{}}}}async function x(e){let t=await c.exiftool.read(e);return{SourceFile:t.SourceFile,ExifToolVersion:t.ExifToolVersion,MIMEType:t.MIMEType,FileType:t.FileType,Make:t.Make,Model:t.Model,LensModel:t.LensModel,DateTimeOriginal:t.DateTimeOriginal,CreateDate:t.CreateDate,ModifyDate:t.ModifyDate,ImageWidth:t.ImageWidth,ImageHeight:t.ImageHeight,GPSLatitude:t.GPSLatitude,GPSLongitude:t.GPSLongitude,FNumber:t.FNumber,ExposureTime:t.ExposureTime,ISO:t.ISO,FocalLength:t.FocalLength,warnings:t.warnings||[],errors:t.errors||[]}}function S(e,t){if(t==null)return``;switch(e){case`FNumber`:return`ƒ/${Number(t).toFixed(1)}`;case`ExposureTime`:return typeof t==`number`?t>=1?`${t}s`:`1/${Math.round(1/t)}s`:t.toString();case`ISO`:return`ISO ${t}`;case`FocalLength`:let e=t.toString();return e.endsWith(`mm`)?e:`${e}mm`;case`DateTimeOriginal`:return typeof t==`object`&&t.year?`${t.year}/${t.month}/${t.day}`:t.toString();default:return t.toString()}}async function C(e,t,n){let r=e.properties?.src;if(!r)return;let i=``,a;try{if(y(r)){let e=await b(r);i=e.path,a=e.cleanup}else if(d.isAbsolute(r))i=r;else{let e=d.dirname(t.path);i=d.resolve(e,r)}if(i&&(l.existsSync(i)||y(r))&&(l.existsSync(i)||(i=decodeURIComponent(i)),l.existsSync(i))){let t=await x(i);if(!(t.FNumber||t.ExposureTime||t.ISO))return;let r=typeof n.exif==`object`?n.exif:{},a=r.fields||[`Model`,`LensModel`,`FocalLength`,`FNumber`,`ExposureTime`,`ISO`,`DateTimeOriginal`],o=r.separator||` · `,s=a.map(e=>{let n=t[e];return n?S(e,n):null}).filter(Boolean);if(s.length===0)return;let c=s.join(o),l={...e.properties};e.tagName=`div`,e.properties={className:[`photosuite-item`]},e.children=[{type:`element`,tagName:`img`,properties:l,children:[]},{type:`element`,tagName:`div`,properties:{className:[`photosuite-exif`]},children:[{type:`text`,value:c}]}]}}catch(e){console.warn(`[photosuite] Failed to get EXIF for ${r}:`,e)}finally{a&&await a()}}function w(e={}){return async(t,n)=>{let r=[],i=t=>{t.type===`element`&&t.tagName===`img`&&r.push(C(t,n,e)),t.children&&t.children.forEach(i)};i(t),r.length>0&&await Promise.all(r)}}function T(e){return{name:`photosuite`,hooks:{"astro:config:setup":({injectScript:t,updateConfig:n})=>{t(`page`,`
|
|
2
|
+
import { photosuite } from 'photosuite';
|
|
3
|
+
const __opts = ${JSON.stringify(e)};
|
|
4
|
+
const __run = () => photosuite(__opts);
|
|
5
|
+
__run();
|
|
6
|
+
document.addEventListener('astro:page-load', __run);
|
|
7
|
+
`);let r=[],i=[];i.push([v,e]),e.exif!==!1&&r.push([w,e]);let a={markdown:{}};r.length>0&&(a.markdown.rehypePlugins=r),i.length>0&&(a.markdown.remarkPlugins=i),Object.keys(a.markdown).length>0&&n(a)}}}}exports.default=T,exports.exiftoolVendored=w,exports.imageUrl=v;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { exiftool } from "exiftool-vendored";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as fsp from "node:fs/promises";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import * as http from "node:http";
|
|
6
|
+
import * as https from "node:https";
|
|
7
|
+
import * as os from "node:os";
|
|
8
|
+
import { URL } from "node:url";
|
|
9
|
+
var join = (...e) => {
|
|
10
|
+
let s = e.filter((e) => typeof e == "string" && e !== "");
|
|
11
|
+
if (s.length === 0) return "";
|
|
12
|
+
if (s.length === 1) return s[0];
|
|
13
|
+
let c = s[0], l = s[1], u = `${c.endsWith("/") ? c.slice(0, -1) : c}/${l.startsWith("/") ? l.slice(1) : l}`;
|
|
14
|
+
return s.length > 2 ? join(u, ...s.slice(2)) : u;
|
|
15
|
+
}, isShort = (e) => !(!e || /^https?:\/\//i.test(e) || e.startsWith("/") || e.startsWith("./") || e.startsWith("../"));
|
|
16
|
+
function imageUrl(e = {}) {
|
|
17
|
+
let { imageBase: s, imageDir: c = "imageDir", fileDir: l = !1 } = e;
|
|
18
|
+
return (e, u) => {
|
|
19
|
+
let d = (u?.data?.astro?.frontmatter || u?.data?.frontmatter || {})[c] || "", f = (u?.path || u?.history?.[0] || "").split(/[\\/]/).pop()?.split(".").shift() || "", p = l ? f : d, g = (e) => {
|
|
20
|
+
if (e) {
|
|
21
|
+
if (e.type === "image") {
|
|
22
|
+
let c = e.url || "";
|
|
23
|
+
(s || p) && isShort(c) && (e.url = join(s, p, c));
|
|
24
|
+
}
|
|
25
|
+
e.children && e.children.length && e.children.forEach(g);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
g(e);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function isHttpUrl(e) {
|
|
32
|
+
try {
|
|
33
|
+
let s = new URL(e);
|
|
34
|
+
return s.protocol === "http:" || s.protocol === "https:";
|
|
35
|
+
} catch {
|
|
36
|
+
return !1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function downloadToTemp(e) {
|
|
40
|
+
let p = os.tmpdir(), m = path.extname(e) || ".bin", h = path.join(p, `exif-${Date.now()}-${Math.random().toString(36).slice(2)}${m}`), g = fs.createWriteStream(h), _ = e.startsWith("https:") ? https : http;
|
|
41
|
+
return await new Promise((s, c) => {
|
|
42
|
+
_.get(e, (e) => {
|
|
43
|
+
if (e.statusCode && e.statusCode >= 300 && e.statusCode < 400 && e.headers.location, e.statusCode !== 200) {
|
|
44
|
+
c(/* @__PURE__ */ Error("HTTP " + e.statusCode));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
e.pipe(g), g.on("finish", s), g.on("error", c);
|
|
48
|
+
}).on("error", c);
|
|
49
|
+
}), {
|
|
50
|
+
path: h,
|
|
51
|
+
cleanup: async () => {
|
|
52
|
+
try {
|
|
53
|
+
await fsp.unlink(h);
|
|
54
|
+
} catch {}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async function handleExif(s) {
|
|
59
|
+
let c = await exiftool.read(s);
|
|
60
|
+
return {
|
|
61
|
+
SourceFile: c.SourceFile,
|
|
62
|
+
ExifToolVersion: c.ExifToolVersion,
|
|
63
|
+
MIMEType: c.MIMEType,
|
|
64
|
+
FileType: c.FileType,
|
|
65
|
+
Make: c.Make,
|
|
66
|
+
Model: c.Model,
|
|
67
|
+
LensModel: c.LensModel,
|
|
68
|
+
DateTimeOriginal: c.DateTimeOriginal,
|
|
69
|
+
CreateDate: c.CreateDate,
|
|
70
|
+
ModifyDate: c.ModifyDate,
|
|
71
|
+
ImageWidth: c.ImageWidth,
|
|
72
|
+
ImageHeight: c.ImageHeight,
|
|
73
|
+
GPSLatitude: c.GPSLatitude,
|
|
74
|
+
GPSLongitude: c.GPSLongitude,
|
|
75
|
+
FNumber: c.FNumber,
|
|
76
|
+
ExposureTime: c.ExposureTime,
|
|
77
|
+
ISO: c.ISO,
|
|
78
|
+
FocalLength: c.FocalLength,
|
|
79
|
+
warnings: c.warnings || [],
|
|
80
|
+
errors: c.errors || []
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function formatField(e, s) {
|
|
84
|
+
if (s == null) return "";
|
|
85
|
+
switch (e) {
|
|
86
|
+
case "FNumber": return `ƒ/${Number(s).toFixed(1)}`;
|
|
87
|
+
case "ExposureTime": return typeof s == "number" ? s >= 1 ? `${s}s` : `1/${Math.round(1 / s)}s` : s.toString();
|
|
88
|
+
case "ISO": return `ISO ${s}`;
|
|
89
|
+
case "FocalLength":
|
|
90
|
+
let e = s.toString();
|
|
91
|
+
return e.endsWith("mm") ? e : `${e}mm`;
|
|
92
|
+
case "DateTimeOriginal": return typeof s == "object" && s.year ? `${s.year}/${s.month}/${s.day}` : s.toString();
|
|
93
|
+
default: return s.toString();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function processNode(e, c, u) {
|
|
97
|
+
let d = e.properties?.src;
|
|
98
|
+
if (!d) return;
|
|
99
|
+
let f = "", p;
|
|
100
|
+
try {
|
|
101
|
+
if (isHttpUrl(d)) {
|
|
102
|
+
let e = await downloadToTemp(d);
|
|
103
|
+
f = e.path, p = e.cleanup;
|
|
104
|
+
} else if (path.isAbsolute(d)) f = d;
|
|
105
|
+
else {
|
|
106
|
+
let e = path.dirname(c.path);
|
|
107
|
+
f = path.resolve(e, d);
|
|
108
|
+
}
|
|
109
|
+
if (f && (fs.existsSync(f) || isHttpUrl(d)) && (fs.existsSync(f) || (f = decodeURIComponent(f)), fs.existsSync(f))) {
|
|
110
|
+
let s = await handleExif(f);
|
|
111
|
+
if (!(s.FNumber || s.ExposureTime || s.ISO)) return;
|
|
112
|
+
let c = typeof u.exif == "object" ? u.exif : {}, l = c.fields || [
|
|
113
|
+
"Model",
|
|
114
|
+
"LensModel",
|
|
115
|
+
"FocalLength",
|
|
116
|
+
"FNumber",
|
|
117
|
+
"ExposureTime",
|
|
118
|
+
"ISO",
|
|
119
|
+
"DateTimeOriginal"
|
|
120
|
+
], d = c.separator || " · ", p = l.map((e) => {
|
|
121
|
+
let c = s[e];
|
|
122
|
+
return c ? formatField(e, c) : null;
|
|
123
|
+
}).filter(Boolean);
|
|
124
|
+
if (p.length === 0) return;
|
|
125
|
+
let m = p.join(d), h = { ...e.properties };
|
|
126
|
+
e.tagName = "div", e.properties = { className: ["photosuite-item"] }, e.children = [{
|
|
127
|
+
type: "element",
|
|
128
|
+
tagName: "img",
|
|
129
|
+
properties: h,
|
|
130
|
+
children: []
|
|
131
|
+
}, {
|
|
132
|
+
type: "element",
|
|
133
|
+
tagName: "div",
|
|
134
|
+
properties: { className: ["photosuite-exif"] },
|
|
135
|
+
children: [{
|
|
136
|
+
type: "text",
|
|
137
|
+
value: m
|
|
138
|
+
}]
|
|
139
|
+
}];
|
|
140
|
+
}
|
|
141
|
+
} catch (e) {
|
|
142
|
+
console.warn(`[photosuite] Failed to get EXIF for ${d}:`, e);
|
|
143
|
+
} finally {
|
|
144
|
+
p && await p();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function exiftoolVendored(e = {}) {
|
|
148
|
+
return async (s, c) => {
|
|
149
|
+
let l = [], u = (s) => {
|
|
150
|
+
s.type === "element" && s.tagName === "img" && l.push(processNode(s, c, e)), s.children && s.children.forEach(u);
|
|
151
|
+
};
|
|
152
|
+
u(s), l.length > 0 && await Promise.all(l);
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function astroPhotosuite(e) {
|
|
156
|
+
return {
|
|
157
|
+
name: "photosuite",
|
|
158
|
+
hooks: { "astro:config:setup": ({ injectScript: s, updateConfig: c }) => {
|
|
159
|
+
s("page", `
|
|
160
|
+
import { photosuite } from 'photosuite';
|
|
161
|
+
const __opts = ${JSON.stringify(e)};
|
|
162
|
+
const __run = () => photosuite(__opts);
|
|
163
|
+
__run();
|
|
164
|
+
document.addEventListener('astro:page-load', __run);
|
|
165
|
+
`);
|
|
166
|
+
let l = [], u = [];
|
|
167
|
+
u.push([imageUrl, e]), e.exif !== !1 && l.push([exiftoolVendored, e]);
|
|
168
|
+
let d = { markdown: {} };
|
|
169
|
+
l.length > 0 && (d.markdown.rehypePlugins = l), u.length > 0 && (d.markdown.remarkPlugins = u), Object.keys(d.markdown).length > 0 && c(d);
|
|
170
|
+
} }
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
export { astroPhotosuite as default, exiftoolVendored, imageUrl };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file exiftoolVendored.ts
|
|
3
|
+
* @description Rehype 插件,用于在编译时使用 exiftool-vendored 提取图片 EXIF 信息并注入到 HTML 中
|
|
4
|
+
*/
|
|
5
|
+
interface Node {
|
|
6
|
+
type: string;
|
|
7
|
+
tagName?: string;
|
|
8
|
+
properties?: Record<string, any>;
|
|
9
|
+
children?: Node[];
|
|
10
|
+
value?: string;
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* ExiftoolVendored Rehype 插件
|
|
15
|
+
*
|
|
16
|
+
* 遍历 HTML AST,查找 img 标签,提取 EXIF 信息并注入到 DOM 结构中
|
|
17
|
+
*
|
|
18
|
+
* @param options - 插件配置项
|
|
19
|
+
* @returns Transformer 函数
|
|
20
|
+
*/
|
|
21
|
+
export declare function exiftoolVendored(options?: any): (tree: Node, file: any) => Promise<void>;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ImageUrlOptions } from '../types';
|
|
2
|
+
interface Node {
|
|
3
|
+
type: string;
|
|
4
|
+
url?: string;
|
|
5
|
+
children?: Node[];
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* ImageUrl Remark 插件
|
|
10
|
+
*
|
|
11
|
+
* 遍历 Markdown AST,查找 image 节点,并根据配置补全其 url 属性
|
|
12
|
+
*
|
|
13
|
+
* @param options - 插件配置项
|
|
14
|
+
* @returns Transformer 函数
|
|
15
|
+
*/
|
|
16
|
+
export declare function imageUrl(options?: ImageUrlOptions): (tree: Node, file: any) => void;
|
|
17
|
+
export {};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file types.ts
|
|
3
|
+
* @description 定义 Photosuite 及其模块使用的接口和类型定义
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Photosuite 主配置项接口
|
|
7
|
+
*/
|
|
8
|
+
export interface PhotosuiteOptions extends ImageUrlOptions {
|
|
9
|
+
/**
|
|
10
|
+
* 生效范围
|
|
11
|
+
* @description 仅限 CSS 选择器范围内生效,支持多值逗号分隔
|
|
12
|
+
*/
|
|
13
|
+
scope: string;
|
|
14
|
+
/**
|
|
15
|
+
* 图片选择器
|
|
16
|
+
* @default "a.glightbox"
|
|
17
|
+
* @description 用于选择需要应用 Photosuite 效果的图片或链接元素的选择器字符串
|
|
18
|
+
*/
|
|
19
|
+
selector?: string;
|
|
20
|
+
/**
|
|
21
|
+
* 画廊名称
|
|
22
|
+
* @default "markdown"
|
|
23
|
+
* @description GLightbox 画廊的分组名称,同一组名称的图片可以相互切换
|
|
24
|
+
*/
|
|
25
|
+
gallery?: string;
|
|
26
|
+
/**
|
|
27
|
+
* 是否启用 GLightbox
|
|
28
|
+
* @default true
|
|
29
|
+
* @description 控制是否加载并启用 GLightbox 灯箱功能
|
|
30
|
+
*/
|
|
31
|
+
glightbox?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* GLightbox 配置项
|
|
34
|
+
* @description 传递给 GLightbox 实例的原生配置对象
|
|
35
|
+
*/
|
|
36
|
+
glightboxOptions?: Record<string, unknown>;
|
|
37
|
+
/**
|
|
38
|
+
* 是否启用图片 Alt 标题
|
|
39
|
+
* @default true
|
|
40
|
+
* @description 控制是否将图片的 alt 属性作为标题显示在图片下方
|
|
41
|
+
*/
|
|
42
|
+
imageAlts?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* 是否启用 EXIF 信息
|
|
45
|
+
* @default true
|
|
46
|
+
* @description 控制是否显示图片的 EXIF 信息。在 Markdown 中使用时,会在编译时自动提取并嵌入 EXIF 数据。
|
|
47
|
+
* 可以传递对象进行更细粒度的配置。
|
|
48
|
+
*/
|
|
49
|
+
exif?: boolean | PhotosuiteExifOptions;
|
|
50
|
+
/**
|
|
51
|
+
* GLightbox CSS CDN 地址
|
|
52
|
+
* @description 自定义 GLightbox CSS 文件的加载地址
|
|
53
|
+
*/
|
|
54
|
+
glightboxCssUrl?: string;
|
|
55
|
+
/**
|
|
56
|
+
* GLightbox JS CDN 地址
|
|
57
|
+
* @description 自定义 GLightbox JS 文件的加载地址
|
|
58
|
+
*/
|
|
59
|
+
glightboxJsUrl?: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* GLightbox 模块初始化配置接口
|
|
63
|
+
*/
|
|
64
|
+
export interface GlightboxModuleOptions {
|
|
65
|
+
/**
|
|
66
|
+
* 生效范围
|
|
67
|
+
*/
|
|
68
|
+
scope: string;
|
|
69
|
+
/**
|
|
70
|
+
* 图片选择器
|
|
71
|
+
* @description 需要绑定 GLightbox 事件的元素选择器
|
|
72
|
+
*/
|
|
73
|
+
selector: string;
|
|
74
|
+
/**
|
|
75
|
+
* 画廊名称
|
|
76
|
+
* @description 图片分组名称
|
|
77
|
+
*/
|
|
78
|
+
gallery: string;
|
|
79
|
+
/**
|
|
80
|
+
* GLightbox 原生配置
|
|
81
|
+
*/
|
|
82
|
+
options?: Record<string, unknown>;
|
|
83
|
+
/**
|
|
84
|
+
* CSS 资源地址
|
|
85
|
+
*/
|
|
86
|
+
cssUrl?: string;
|
|
87
|
+
/**
|
|
88
|
+
* JS 资源地址
|
|
89
|
+
*/
|
|
90
|
+
jsUrl?: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* ImageUrl Rehype 插件配置接口
|
|
94
|
+
*/
|
|
95
|
+
export interface ImageUrlOptions {
|
|
96
|
+
/**
|
|
97
|
+
* 图片基础 URL
|
|
98
|
+
* @example "https://cdn.example.com/images/"
|
|
99
|
+
* @description 用于补全图片路径的基础 URL 前缀。
|
|
100
|
+
*/
|
|
101
|
+
imageBase?: string;
|
|
102
|
+
/**
|
|
103
|
+
* 图片目录 Frontmatter Key
|
|
104
|
+
* @default "imageDir"
|
|
105
|
+
* @description 在 Markdown Frontmatter 中指定子目录名称的字段名。
|
|
106
|
+
*/
|
|
107
|
+
imageDir?: string;
|
|
108
|
+
/**
|
|
109
|
+
* 是否使用文件名作为子目录
|
|
110
|
+
* @default false
|
|
111
|
+
* @description 如果为 true,则使用当前 Markdown 文件名(不含扩展名)作为子目录。
|
|
112
|
+
*/
|
|
113
|
+
fileDir?: boolean;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* EXIF 配置选项
|
|
117
|
+
*/
|
|
118
|
+
export interface PhotosuiteExifOptions {
|
|
119
|
+
/**
|
|
120
|
+
* 是否启用
|
|
121
|
+
* @default true
|
|
122
|
+
*/
|
|
123
|
+
enabled?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* 需要展示的 EXIF 字段及其顺序
|
|
126
|
+
* @default ['Model', 'LensModel', 'FocalLength', 'FNumber', 'ExposureTime', 'ISO', 'DateTimeOriginal']
|
|
127
|
+
*/
|
|
128
|
+
fields?: ('Make' | 'Model' | 'LensModel' | 'FocalLength' | 'FNumber' | 'ExposureTime' | 'ISO' | 'DateTimeOriginal')[];
|
|
129
|
+
/**
|
|
130
|
+
* 分隔符
|
|
131
|
+
* @default " · "
|
|
132
|
+
*/
|
|
133
|
+
separator?: string;
|
|
134
|
+
}
|
package/dist/vite.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "photosuite",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A zero-config Astro integration for better images (Lightbox, EXIF, Path resolution).",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"astro-integration",
|
|
7
|
+
"lightbox",
|
|
8
|
+
"exif",
|
|
9
|
+
"gallery",
|
|
10
|
+
"markdown-images",
|
|
11
|
+
"photography",
|
|
12
|
+
"zero-config"
|
|
13
|
+
],
|
|
14
|
+
"author": "achuanya",
|
|
15
|
+
"license": "GPL-3.0",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/achuanya/photosuite"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://photosuite.lhasa.icu",
|
|
21
|
+
"bugs": "https://github.com/achuanya/photosuite/issues",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "./dist/photosuite.integration.js",
|
|
24
|
+
"module": "./dist/photosuite.es.js",
|
|
25
|
+
"types": "./dist/integration.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"node": {
|
|
29
|
+
"types": "./dist/integration.d.ts",
|
|
30
|
+
"import": "./dist/photosuite.integration.js",
|
|
31
|
+
"require": "./dist/photosuite.integration.cjs"
|
|
32
|
+
},
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/photosuite.es.js",
|
|
35
|
+
"require": "./dist/photosuite.cjs.js",
|
|
36
|
+
"default": "./dist/photosuite.es.js"
|
|
37
|
+
},
|
|
38
|
+
"./client": {
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"import": "./dist/photosuite.es.js",
|
|
41
|
+
"require": "./dist/photosuite.cjs.js"
|
|
42
|
+
},
|
|
43
|
+
"./dist/photosuite.css": "./dist/photosuite.css"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"dist",
|
|
47
|
+
"LICENSE",
|
|
48
|
+
"README.md",
|
|
49
|
+
"README.zh-CN.md"
|
|
50
|
+
],
|
|
51
|
+
"scripts": {
|
|
52
|
+
"dev": "vite",
|
|
53
|
+
"build": "tsc && vite build",
|
|
54
|
+
"preview": "vite preview"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"astro": "^4.0.0 || ^5.0.0"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"exiftool-vendored": "^34.1.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@tailwindcss/postcss": "^4.1.17",
|
|
64
|
+
"@types/node": "^25.0.3",
|
|
65
|
+
"autoprefixer": "^10.4.22",
|
|
66
|
+
"postcss": "^8.5.6",
|
|
67
|
+
"sass": "^1.94.2",
|
|
68
|
+
"tailwindcss": "^4.1.17",
|
|
69
|
+
"typescript": "~5.9.3",
|
|
70
|
+
"vite": "npm:rolldown-vite@7.2.5",
|
|
71
|
+
"vite-plugin-dts": "^4.5.4"
|
|
72
|
+
},
|
|
73
|
+
"overrides": {
|
|
74
|
+
"vite": "npm:rolldown-vite@7.2.5"
|
|
75
|
+
}
|
|
76
|
+
}
|