diff --git a/_config.yml b/_config.yml index 2b877c3acb4d..a1cdb90f5f82 100644 --- a/_config.yml +++ b/_config.yml @@ -136,4 +136,3 @@ alias: tags/GCP/: tags/GoogleCloud/ tags/画像認識/: tags/画像処理/ tags/icon: categories/アイコン/ - diff --git a/package-lock.json b/package-lock.json index 65ef4a96e2bf..8182b837e674 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "future-tech-blog", "version": "0.1.0", "dependencies": { + "cheerio": "^1.1.2", "googleapis": "^126.0.1", "hexo": "^7.2.0", "hexo-easy-tags-plugin": "^1.1.0", @@ -31,6 +32,7 @@ "image-size": "^1.0.2", "js-yaml": "^4.1.0", "markdown-it": "^14.1.0", + "node-fetch": "^2.7.0", "npm": "^10.1.0", "random-seed": "^0.3.0", "rss-parser": "^3.13.0", @@ -1037,6 +1039,12 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, "node_modules/boundary": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/boundary/-/boundary-1.0.1.tgz", @@ -1194,6 +1202,198 @@ "integrity": "sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==", "dev": true }, + "node_modules/cheerio": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.2.tgz", + "integrity": "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.0.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.12.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cheerio-select/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/cheerio-select/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/cheerio-select/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/cheerio-select/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/htmlparser2": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.1", + "entities": "^6.0.0" + } + }, + "node_modules/cheerio/node_modules/htmlparser2/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -1505,6 +1705,89 @@ "node": "*" } }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-select/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/css-select/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/css-select/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/css-select/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", @@ -1776,6 +2059,31 @@ "node": ">= 0.8" } }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/encoding-sniffer/node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/entities": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", @@ -4985,6 +5293,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -8125,6 +8434,18 @@ "inBundle": true, "license": "ISC" }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/nunjucks": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", @@ -8308,20 +8629,62 @@ } }, "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "license": "MIT", "dependencies": { - "entities": "^4.4.0" + "parse5": "^7.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" } }, "node_modules/parse5/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -10921,6 +11284,15 @@ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", "license": "MIT" }, + "node_modules/undici": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.16.0.tgz", + "integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/unicorn-magic": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", diff --git a/package.json b/package.json index a3b4728e2dc4..4538cb85bcdc 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "version": "7.2.0" }, "dependencies": { + "cheerio": "^1.1.2", "googleapis": "^126.0.1", "hexo": "^7.2.0", "hexo-easy-tags-plugin": "^1.1.0", @@ -29,6 +30,7 @@ "image-size": "^1.0.2", "js-yaml": "^4.1.0", "markdown-it": "^14.1.0", + "node-fetch": "^2.7.0", "npm": "^10.1.0", "random-seed": "^0.3.0", "rss-parser": "^3.13.0", @@ -49,4 +51,4 @@ "lint": { "terraform_guidelines.md": "textlint" } -} \ No newline at end of file +} diff --git a/scripts/autolink_image_source.js b/scripts/autolink_image_source.js new file mode 100644 index 000000000000..8d723a7bb3fd --- /dev/null +++ b/scripts/autolink_image_source.js @@ -0,0 +1,30 @@ +/** + * タグの直後にある「出典:URL...」形式のテキストを、複数のURLに対応して自動でリンク化するフィルター + */ +hexo.extend.filter.register('after_post_render', function(data) { + // 投稿の本文(data.content)に対して処理を行う + if (!data || !data.content) { + return; + } + + // タグ、間のテキスト、「出典:」、そしてその後のURL群を含むテキスト、というパターンを探す正規表現 + // [^<]+ は、次のHTMLタグが現れるまでの全てのテキストをキャプチャする + const blockRegex = /(]+>)([\s\S]*?出典:)([^<]+)/g; + + data.content = data.content.replace(blockRegex, (match, imgTag, sourcePrefix, urlsText) => { + + // キャプチャしたURL群のテキスト (urlsText) の中から、個々のURLを探すための正規表現 + const urlRegex = /(https?:\/\/[^\s<]+)/g; + + // 見つかった全てのURLをタグで囲むように置換する + // urlパラメータにはマッチした個々のURL文字列が入る + const linkedUrls = urlsText.trim().replace(urlRegex, (url) => { + return `${url}`; + }); + + // 元のタグ + 「出典:」までのテキスト + リンク化されたURL群 を結合して返す + return imgTag + sourcePrefix + ' ' + linkedUrls; + }); + + return data; +}); diff --git a/scripts/preview.js b/scripts/preview.js new file mode 100644 index 000000000000..89f4cd5d2cb2 --- /dev/null +++ b/scripts/preview.js @@ -0,0 +1,76 @@ +const fetch = require('node-fetch'); +const cheerio = require('cheerio'); + +async function getOgpData(url) { + try { + const response = await fetch(url, { + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36' + } + }); + if (!response.ok) { + throw new Error(`Failed to fetch: ${response.statusText}`); + } + const html = await response.text(); + const $ = cheerio.load(html); + + const getMetaContent = (prop) => $(`meta[property="${prop}"]`).attr('content') || $(`meta[name="${prop}"]`).attr('content'); + + let description = getMetaContent('og:description') || getMetaContent('description'); + if (description === 'NULL') { + description = ''; + } + + const ogp = { + title: getMetaContent('og:title') || $('title').text(), + description: description, // 取得したdescriptionを設定 + image: getMetaContent('og:image'), + siteName: getMetaContent('og:site_name'), + favicon: $('link[rel="icon"], link[rel="shortcut icon"]').attr('href') + }; + + if (ogp.favicon && !ogp.favicon.startsWith('http')) { + const urlObj = new URL(url); + ogp.favicon = `${urlObj.protocol}//${urlObj.hostname}${ogp.favicon}`; + } + + return ogp; + } catch (error) { + console.error(`Error fetching OGP data for ${url}:`, error); + return null; + } +} + +hexo.extend.tag.register('link_preview', async function(args) { + const url = args[0]; + if (!url) return ''; + + const data = await getOgpData(url); + + if (!data || !data.title) { + return `${url}`; + } + + // 出力時に必ず空文字をフォールバックにする + const title = data.title || ''; + const description = data.description || ''; + const image = data.image; + const siteName = data.siteName || new URL(url).hostname; + const favicon = data.favicon; + + return ` + + `; +}, {async: true}); diff --git "a/source/_posts/2025/20250916a_\343\200\214GOOD_DESIGN_EXHIBITION_2024\343\200\215\343\201\253\345\217\202\345\212\240\343\201\227\343\201\246\343\201\215\343\201\276\343\201\227\343\201\237\357\274\201.md" "b/source/_posts/2025/20250916a_\343\200\214GOOD_DESIGN_EXHIBITION_2024\343\200\215\343\201\253\345\217\202\345\212\240\343\201\227\343\201\246\343\201\215\343\201\276\343\201\227\343\201\237\357\274\201.md" new file mode 100644 index 000000000000..74a738d73212 --- /dev/null +++ "b/source/_posts/2025/20250916a_\343\200\214GOOD_DESIGN_EXHIBITION_2024\343\200\215\343\201\253\345\217\202\345\212\240\343\201\227\343\201\246\343\201\215\343\201\276\343\201\227\343\201\237\357\274\201.md" @@ -0,0 +1,206 @@ +--- +title: "「GOOD DESIGN EXHIBITION 2024」に参加してきました!" +date: 2025/09/16 00:00:00 +postid: a +tag: + - 展示会 + - 参加レポート +category: + - Business +thumbnail: /images/2025/20250916a/thumbnail.png +author: 福井彩乃 +lede: "通常業務の傍ら、DXチームの有志メンバでチーム全体の資料品質向上のためのアセット化活動を行っています。今回、その活動の一環として2024年11月に東京ミッドタウンで開催された「GOOD DESIGN EXHIBITION 2024」に参加してきました。この記事では、グッドデザイン賞およびその受賞事例についてご紹介します。" +--- +## はじめに + +Technology Innovation Group DXチームの福井です。 + +通常業務の傍ら、DXチームの有志メンバでチーム全体の資料品質向上のためのアセット化活動を行っています。 + +今回、その活動の一環として2024年11月に東京ミッドタウンで開催された「GOOD DESIGN EXHIBITION 2024」に参加してきました。 + +この記事では、グッドデザイン賞およびその受賞事例についてご紹介します。みなさんに「デザイン」を身近なものに感じていただき、興味を持っていただくきっかけになればと思います。 + +- [GOOD DESIGN EXHIBITION 2024 | DESIGN TOUCH 2024](https://www.tokyo-midtown.com/jp/event/designtouch/gooddesignexhibition.html) + +## 「グッドデザイン賞」とは? + +「Gマーク」でもおなじみの「グッドデザイン賞」は、優れたデザインを通じて社会をより良くしていくことを目指す、日本で唯一の総合的なデザイン評価制度です。 +その歴史は60年以上にわたります。 + +- [GOOD DESIGN AWARD](https://www.g-mark.org/) + +日本には「グッドデザイン賞」以外にも、優れたデザインを評価するアワードが多く存在します。 +例えば、革新的な優れたサービスを対象とした「日本サービス大賞」、空間そのものを対象とした「日本空間デザイン賞」、文具・家具・道具全般を対象とした「コクヨデザインアワード」などがあります。 + +そのような数あるデザイン賞の中で「グッドデザイン賞」の最大の特徴は、審査対象が非常に幅広いことです。工業製品のような「有形」のプロダクトだけでなく、サービス、ソフトウェア、アプリケーション、さらには地域づくりや社会貢献活動といった「無形」のデザインまで、私たちの暮らしや社会を豊かにするすべてのものが評価の対象となっています。 + +## 会場の様子 + +東京ミッドタウン内の複数エリアに2024年の全受賞作品が展示されており、かなりのボリュームがありました。 +プロダクトの展示、パネルでのサービス説明、実際に商品が購入できるポップアップストアなど、コンテンツは多岐にわたります。実際に触ったり、体験できるプロダクトもありました。 + +image.png + +- [ASUS Zenbook DUO (2024)](https://www.g-mark.org/gallery/winners/23827) +- [ゲルソノマ 15-50/1201A727](https://www.g-mark.org/gallery/winners/24475?years=2024) +- [CMF by Nothing Product Range](https://www.g-mark.org/gallery/winners/23510) +- [ざる屋の盆ざる](https://www.g-mark.org/gallery/winners/27448) +- [福岡市認知症の人にもやさしいデザイン](https://www.g-mark.org/gallery/winners/25503) +- [デジロー (デジタル スシロービジョン)](https://www.g-mark.org/gallery/winners/22821?years=2024) + +## 時代と共に変化する「良いデザイン」の基準 + +グッドデザイン賞の審査基準は、時代の変化に伴って変化し続けてきました。 +各時代における「良いデザイン」の変遷をご紹介します。 + +### 1960年代 + +高度経済成長期。1964年の東京オリンピック開催に伴い、街中にはピクトグラムが設置され「デザイン」という言葉が浸透し始めた時代。「美的感覚」「独創性」などが重視されました。1960年代中盤のいざなぎ景気時代には、新三種の神器と呼ばれる車、エアコン、カラーテレビなどが広く普及し、経済大国へと成長していきました。 + +{% link_preview https://www.g-mark.org/gallery/winners/9d03dfe8-803d-11ed-862b-0242ac130002 %} + +### 1970-80年代 + +大阪万博を皮切りに日本の「ものづくり」が世界的に評価された時代。ソニーからは1979年にウォークマン、本田技研工業からは1981年にシティなど、世界に対して発信力の高い製品が数多く登場するようになりました。デザインに「品質水準」「機能性」「安全性」といった尺度が加わりました。 + +{% link_preview https://www.g-mark.org/gallery/winners/9c6b0333-803d-11ed-862b-0242ac130002 %} + +### 1990年代 + +インターネットが普及し価値が多様化した時代。またスーパーファミコンや、プリクラ、4足歩行ロボット「AIBO」など電子機器が生活になくてはならないものとなり、価値変化の時代となりました。「生活者」や「ライフスタイル」がデザインで重要視される要素になりました。 + +{% link_preview https://www.g-mark.org/gallery/winners/9cf81f93-803d-11ed-862b-0242ac130002 %} + +### 2000年代 + +「持続可能な開発に関する世界首脳会議(WSSD)」が開催され、地球温暖化などエコ問題が意識され始めた時代。社会課題が顕在化し、「エコロジーデザイン」や「ユニバーサルデザイン」など、社会的な視点が強まりました。トヨタのプリウスは、高性能な新世代ハイブリッドシステムにより世界最高レベルとなる低燃費、低エミッションを追求し、環境性能という新たな価値を生み出しました。 + +{% link_preview https://www.g-mark.org/gallery/winners/9d19150c-803d-11ed-862b-0242ac130002 %} + +### 2010年代 + +2011年に発生した東日本大震災をきっかけに、防災、地域社会への意識の高まりなど、さまざまな変化が見られた時代。また国内のインターネットの利用者は1億人を超え、音声認識AIの開発や、キャッシュレスの浸透、ドローンなど新しい技術が登場しました。「社会参加」や「創発」が重要なテーマとなりました。 + +{% link_preview https://www.g-mark.org/gallery/winners/9ddfe3a3-803d-11ed-af7e-0242ac130002 %} + +### 2020年代 + +新型コロナウイルス感染症(COVID-19)の世界的流行により、マスクの着用、ソーシャルディスタンス、また職場や学校などあらゆる場所でオンライン環境が取り入れられるなど「新しい生活様式」が実践されました。また、SDGsの浸透により「持続可能性」のあるデザインが求められるようになりました。 + +{% link_preview https://www.g-mark.org/gallery/winners/9e389033-803d-11ed-af7e-0242ac130002 %} + +出典:https://www.g-mark.org/learn/gda/statistics/screening-criteria + +そして2024年時点では、以下の4つの視点から総合的に審査が行われています。 + +> #### 👤 人間的視点 +> +> - 使いやすさ・分かりやすさ・親切さなど、ユーザーに対してしかるべき配慮が行われているか +> - 安全・安心・環境・身体的弱者など、信頼性を確保するための様々な配慮が行われているか +> - ユーザーから共感を得るデザインであるか +> - 魅力を有し、ユーザーの創造性を誘発するデザインであるか +> +> #### 🏭 産業的視点 +> +> - 新技術・新素材などを利用または創意工夫によりたくみに課題を解決しているか +> - 的確な技術・方法・品質で合理的に設計・計画されているか +> - 新産業、新ビジネスの創出に貢献しているか +> +> #### 🌱 社会的視点 +> +> - 新しい作法、ライフスタイル、コミュニケーションなど、新たな文化の創出に貢献しているか +> - 持続可能な社会の実現に対して貢献しているか +> - 新たな手法、概念、様式など、社会に対して新たな価値を提案しているか +> +> #### ⏳ 時間的視点 +> +> - 過去の文脈や蓄積を活かし、新たな価値を提案しているか +> - 中・長期的な観点から持続可能性の高い提案が行われているか +> - 時代に即した改善を継続しているか +> +>出典:https://www.g-mark.org/apply/gda/screening/perspective + +## 受賞事例から見る「良いデザイン」のカタチ + +では、具体的にどのようなものが受賞しているのでしょうか。個人的にいいなと感じた3つの事例をご紹介します。 + +### 1. スマートフォンアプリ - みみみ + +https://www.g-mark.org/gallery/winners/26906 + +「みみみ」はデジタル世代の興味・関心に合わせて地域ニュースを届け、理解を深める体験を提供するアプリです。 デジタル世代の普段使いに馴染む洗練されたデザイン性、スムーズに使えるインターフェースで操作も簡単。 +スワイプしながら手軽にニュースを追える「コレだけ」機能や、浮かんだ疑問を記者に直接質問できる「イドバタ」機能が特徴で、一方的な情報提供ではない、地域に根付いた新聞社ならではの新しいニュース体験の形を示しています。 + +#### 直感的なニュース体験 + +デジタル世代の普段使いに馴染む洗練されたデザイン性、スムーズに使えるインターフェースで操作も簡単。ストレスフリーでニュース体験を楽しめます。 + +image.png +出典: https://tarupo.jp/mimimi + +#### 記者が「?」にこたえる! + +話題やコメントが投稿できる「イドバタ」機能。同じ街に住む記者が、身の回りのニュースについて答え、一緒に考えてくれることも。 + +image.png +出典:https://tarupo.jp/mimimi + +1400 + +ちなみに、「みみみ」のユーザ基盤を支えている「たるポ」はFutureとも関わりがあります🎉 + +- [たるポ このまち応援プラットフォーム](https://tarupo.jp/) +- [中国新聞社、フューチャーアーキテクトとともに中国地方の地域活性化を推進](https://prtimes.jp/main/html/rd/p/000000012.000105470.html) + +### 2. 旅行サービス - どこかにビューーン! + +https://www.g-mark.org/gallery/winners/17585 + +JRE POINT 6,000ポイントで、JR東日本が管轄する47駅の中からランダムに選ばれた「どこか」に行けるサービスです。 +ユーザはお得に新幹線に乗れる、事業者にとっては空席をうまく活用できる、地域にとっては観光資源を知ってもらえる機会が生まれる、という三方よしのサービス。行き先をランダムで選べるワクワク感も楽しいですね。 + +#### 観光流動の均等化 + +観光は特定の地域に人気が集中することも多い。そのような中、新幹線の空席状況に合わせて、行き先を決定することで観光流動の均等化に繋げられます。 + +image.png +
出典:https://dokokani-eki-net.com/ + +#### 新たな地域との出会いを創出 + +利用者が目的地を最終決定できない仕組みとなっており、観光資源の魅力を新たに知る機会を創出しています。 + +image.png +
出典:https://dokokani-eki-net.com/ + +### 3. デザインガイドライン - デジタル庁デザインシステム + +https://www.g-mark.org/gallery/winners/27505 + +行政機関のウェブサイトなどで利用されることを想定し、誰もが平等に利用しやすい社会を実現するべく、アクセシビリティを最優先で構築されたデザインシステムです。身近なものだとマイナンバー関連の手続きアプリなどにも導入されています。豊富なガイドラインとコードスニペットが公開されており、開発工程の業務量やコスト削減にも寄与します。 + + + +#### 公共サービスの標準化へ + +カラーパレット、文字の形状やサイズ、イラストやボタン、コンポーネントなどを国際的に推奨されるガイドラインに基づいて整備。マイナンバーの手続きアプリなどで導入されている。 +公共サービスの標準化が進み、誰もが安心してアクセスできる「優しいデジタル社会」の実現が期待されます。 + +image.png +出典:https://design.digital.go.jp/ , https://digital-gov.note.jp/n/n81e591f9c7e7 + +#### 豊富なガイドラインとコードスニペット + +あらかじめアクセシビリティを十分に考慮し、かつデザイン性に優れたデザインライブラリやガイドラインを整備。様々な行政サービスで利用できるようにネット上で公開されています。(動作環境:Webサイト、Figma、GitHub) + +image.png +
出典:https://design.digital.go.jp/ , https://github.com/digital-go-jp/design-system-example-components-html + +## さいごに + +今回はグッドデザイン賞とその評価軸や具体的な事例についてご紹介しました。 + +「良いデザイン」というと、つい「有形」のプロダクトを思い浮かべがちですが、こうして見ると、サービスやシステムといった「無形」のものも多くあり、我々の仕事にも活かせる部分がたくさんありそうです。とっても良い刺激になりました。 + +この記事が、みなさんに「良いデザイン」について考えていただくきっかけになれば幸いです! diff --git a/source/images/2025/20250916a/designsystem.avif b/source/images/2025/20250916a/designsystem.avif new file mode 100644 index 000000000000..bcc81d2dc970 Binary files /dev/null and b/source/images/2025/20250916a/designsystem.avif differ diff --git a/source/images/2025/20250916a/image.png b/source/images/2025/20250916a/image.png new file mode 100644 index 000000000000..0bbb4593ba7e Binary files /dev/null and b/source/images/2025/20250916a/image.png differ diff --git a/source/images/2025/20250916a/image_2.png b/source/images/2025/20250916a/image_2.png new file mode 100644 index 000000000000..a21da3f8fcab Binary files /dev/null and b/source/images/2025/20250916a/image_2.png differ diff --git a/source/images/2025/20250916a/image_3.png b/source/images/2025/20250916a/image_3.png new file mode 100644 index 000000000000..34402c20a12c Binary files /dev/null and b/source/images/2025/20250916a/image_3.png differ diff --git a/source/images/2025/20250916a/image_4.png b/source/images/2025/20250916a/image_4.png new file mode 100644 index 000000000000..2af6a6296b66 Binary files /dev/null and b/source/images/2025/20250916a/image_4.png differ diff --git a/source/images/2025/20250916a/image_5.png b/source/images/2025/20250916a/image_5.png new file mode 100644 index 000000000000..f356289b87dc Binary files /dev/null and b/source/images/2025/20250916a/image_5.png differ diff --git a/source/images/2025/20250916a/image_6.png b/source/images/2025/20250916a/image_6.png new file mode 100644 index 000000000000..c5a007c18e91 Binary files /dev/null and b/source/images/2025/20250916a/image_6.png differ diff --git a/source/images/2025/20250916a/image_7.png b/source/images/2025/20250916a/image_7.png new file mode 100644 index 000000000000..b8cb82725c50 Binary files /dev/null and b/source/images/2025/20250916a/image_7.png differ diff --git a/source/images/2025/20250916a/mimimi.avif b/source/images/2025/20250916a/mimimi.avif new file mode 100644 index 000000000000..9082ebdcf009 Binary files /dev/null and b/source/images/2025/20250916a/mimimi.avif differ diff --git a/source/images/2025/20250916a/thumbnail.png b/source/images/2025/20250916a/thumbnail.png new file mode 100644 index 000000000000..4a4dad06571f Binary files /dev/null and b/source/images/2025/20250916a/thumbnail.png differ diff --git a/themes/future/source/css/theme-styles.styl b/themes/future/source/css/theme-styles.styl index ca6a682540c9..7f84588130d6 100644 --- a/themes/future/source/css/theme-styles.styl +++ b/themes/future/source/css/theme-styles.styl @@ -1169,3 +1169,86 @@ pre.mermaid { background-position: center; transform: translateY(4px); /* テキストラインに合わせるため下に移動させる */ } + +/* リンクプレビューカードのスタイル */ +.link-preview-card { + max-width: 500px; + margin: 1.5em 0; + border: 1px solid #e0e0e0; + border-radius: 8px; + overflow: hidden; + transition: box-shadow 0.3s, transform 0.3s; + background-color: #fff; +} + +.link-preview-card:hover { + box-shadow: 0 4px 12px rgba(0,0,0,0.1); + transform: translateY(-2px); +} + +.link-preview-card-anchor { + display: flex; + text-decoration: none; + color: inherit; + min-height: 110px; +} + +.link-preview-card-main { + padding: 12px 16px; + flex-grow: 1; + display: flex; + flex-direction: column; + justify-content: space-between; + overflow: hidden; +} + +.link-preview-card-title { + font-size: 1em; + font-weight: bold; + margin-bottom: 4px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.link-preview-card-description { + font-size: 0.85em; + color: #555; + line-height: 1.4; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; /* 表示する行数を2行に制限 */ + overflow: hidden; + flex-grow: 1; +} + +.link-preview-card-meta { + display: flex; + align-items: center; + font-size: 0.75em; + color: #888; + margin-top: 8px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.link-preview-card-favicon { + width: 16px; + height: 16px; + margin-right: 6px; + border-radius: 2px; +} + +.link-preview-card-image { + flex-shrink: 0; + width: 110px; + background-size: cover; + background-position: center; + border-left: 1px solid #e0e0e0; +} + +/* 画像がない場合のスタイル調整 */ +.link-preview-card-anchor:not(:has(.link-preview-card-image)) .link-preview-card-main { + width: 100%; +}