vercel deploy fix
3
.gitignore
vendored
|
|
@ -22,3 +22,6 @@ pnpm-debug.log*
|
||||||
|
|
||||||
# jetbrains setting folder
|
# jetbrains setting folder
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# vercel build output (generated by the adapter)
|
||||||
|
.vercel/
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
//#region \0virtual:astro:server-island-manifest
|
|
||||||
var serverIslandMap = /* @__PURE__ */ new Map([]);
|
|
||||||
var serverIslandNameMap = /* @__PURE__ */ new Map([]);
|
|
||||||
//#endregion
|
|
||||||
export { serverIslandMap, serverIslandNameMap };
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
//#region \0virtual:astro:session-driver
|
|
||||||
var _virtual_astro_session_driver_default = null;
|
|
||||||
//#endregion
|
|
||||||
export { _virtual_astro_session_driver_default as default };
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
import { Z as AstroError, y as InvalidComponentArgs } from "./errors-data_zYnyWRO_.mjs";
|
|
||||||
import "./jsx-runtime_DseUv9uh.mjs";
|
|
||||||
//#region \0rolldown/runtime.js
|
|
||||||
var __defProp = Object.defineProperty;
|
|
||||||
var __exportAll = (all, no_symbols) => {
|
|
||||||
let target = {};
|
|
||||||
for (var name in all) __defProp(target, name, {
|
|
||||||
get: all[name],
|
|
||||||
enumerable: true
|
|
||||||
});
|
|
||||||
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
||||||
return target;
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/runtime/server/astro-component.js
|
|
||||||
function validateArgs(args) {
|
|
||||||
if (args.length !== 3) return false;
|
|
||||||
if (!args[0] || typeof args[0] !== "object") return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
function baseCreateComponent(cb, moduleId, propagation) {
|
|
||||||
const name = moduleId?.split("/").pop()?.replace(".astro", "") ?? "";
|
|
||||||
const fn = (...args) => {
|
|
||||||
if (!validateArgs(args)) throw new AstroError({
|
|
||||||
...InvalidComponentArgs,
|
|
||||||
message: InvalidComponentArgs.message(name)
|
|
||||||
});
|
|
||||||
return cb(...args);
|
|
||||||
};
|
|
||||||
Object.defineProperty(fn, "name", {
|
|
||||||
value: name,
|
|
||||||
writable: false
|
|
||||||
});
|
|
||||||
fn.isAstroComponentFactory = true;
|
|
||||||
fn.moduleId = moduleId;
|
|
||||||
fn.propagation = propagation;
|
|
||||||
return fn;
|
|
||||||
}
|
|
||||||
function createComponentWithOptions(opts) {
|
|
||||||
return baseCreateComponent(opts.factory, opts.moduleId, opts.propagation);
|
|
||||||
}
|
|
||||||
function createComponent(arg1, moduleId, propagation) {
|
|
||||||
if (typeof arg1 === "function") return baseCreateComponent(arg1, moduleId, propagation);
|
|
||||||
else return createComponentWithOptions(arg1);
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
export { __exportAll as n, createComponent as t };
|
|
||||||
|
|
@ -1,386 +0,0 @@
|
||||||
//#region node_modules/astro/dist/core/errors/utils.js
|
|
||||||
function normalizeLF(code) {
|
|
||||||
return code.replace(/\r\n|\r(?!\n)|\n/g, "\n");
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/core/errors/printer.js
|
|
||||||
function codeFrame(src, loc) {
|
|
||||||
if (!loc || loc.line === void 0 || loc.column === void 0) return "";
|
|
||||||
const lines = normalizeLF(src).split("\n").map((ln) => ln.replace(/\t/g, " "));
|
|
||||||
const visibleLines = [];
|
|
||||||
for (let n = -2; n <= 2; n++) if (lines[loc.line + n]) visibleLines.push(loc.line + n);
|
|
||||||
let gutterWidth = 0;
|
|
||||||
for (const lineNo of visibleLines) {
|
|
||||||
let w = `> ${lineNo}`;
|
|
||||||
if (w.length > gutterWidth) gutterWidth = w.length;
|
|
||||||
}
|
|
||||||
let output = "";
|
|
||||||
for (const lineNo of visibleLines) {
|
|
||||||
const isFocusedLine = lineNo === loc.line - 1;
|
|
||||||
output += isFocusedLine ? "> " : " ";
|
|
||||||
output += `${lineNo + 1} | ${lines[lineNo]}
|
|
||||||
`;
|
|
||||||
if (isFocusedLine) output += `${Array.from({ length: gutterWidth }).join(" ")} | ${Array.from({ length: loc.column }).join(" ")}^
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/core/errors/errors.js
|
|
||||||
function isAstroError(e) {
|
|
||||||
return e != null && (e instanceof AstroError || AstroError.is(e));
|
|
||||||
}
|
|
||||||
var AstroError = class extends Error {
|
|
||||||
loc;
|
|
||||||
title;
|
|
||||||
hint;
|
|
||||||
frame;
|
|
||||||
type = "AstroError";
|
|
||||||
constructor(props, options) {
|
|
||||||
const { name, title, message, stack, location, hint, frame } = props;
|
|
||||||
super(message, options);
|
|
||||||
this.title = title;
|
|
||||||
this.name = name;
|
|
||||||
if (message) this.message = message;
|
|
||||||
this.stack = stack ? stack : this.stack;
|
|
||||||
this.loc = location;
|
|
||||||
this.hint = hint;
|
|
||||||
this.frame = frame;
|
|
||||||
}
|
|
||||||
setLocation(location) {
|
|
||||||
this.loc = location;
|
|
||||||
}
|
|
||||||
setName(name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
setMessage(message) {
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
setHint(hint) {
|
|
||||||
this.hint = hint;
|
|
||||||
}
|
|
||||||
setFrame(source, location) {
|
|
||||||
this.frame = codeFrame(source, location);
|
|
||||||
}
|
|
||||||
static is(err) {
|
|
||||||
return err?.type === "AstroError";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var AstroUserError = class extends Error {
|
|
||||||
type = "AstroUserError";
|
|
||||||
/**
|
|
||||||
* A message that explains to the user how they can fix the error.
|
|
||||||
*/
|
|
||||||
hint;
|
|
||||||
name = "AstroUserError";
|
|
||||||
constructor(message, hint) {
|
|
||||||
super();
|
|
||||||
this.message = message;
|
|
||||||
this.hint = hint;
|
|
||||||
}
|
|
||||||
static is(err) {
|
|
||||||
return err?.type === "AstroUserError";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/core/errors/errors-data.js
|
|
||||||
var ClientAddressNotAvailable = {
|
|
||||||
name: "ClientAddressNotAvailable",
|
|
||||||
title: "`Astro.clientAddress` is not available in current adapter.",
|
|
||||||
message: (adapterName) => `\`Astro.clientAddress\` is not available in the \`${adapterName}\` adapter. File an issue with the adapter to add support.`
|
|
||||||
};
|
|
||||||
var PrerenderClientAddressNotAvailable = {
|
|
||||||
name: "PrerenderClientAddressNotAvailable",
|
|
||||||
title: "`Astro.clientAddress` cannot be used inside prerendered routes.",
|
|
||||||
message: (name) => `\`Astro.clientAddress\` cannot be used inside prerendered route ${name}.`
|
|
||||||
};
|
|
||||||
var StaticClientAddressNotAvailable = {
|
|
||||||
name: "StaticClientAddressNotAvailable",
|
|
||||||
title: "`Astro.clientAddress` is not available in prerendered pages.",
|
|
||||||
message: "`Astro.clientAddress` is only available on pages that are server-rendered.",
|
|
||||||
hint: "See https://docs.astro.build/en/guides/on-demand-rendering/ for more information on how to enable SSR."
|
|
||||||
};
|
|
||||||
var NoMatchingStaticPathFound = {
|
|
||||||
name: "NoMatchingStaticPathFound",
|
|
||||||
title: "No static path found for requested path.",
|
|
||||||
message: (pathName) => `A \`getStaticPaths()\` route pattern was matched, but no matching static path was found for requested path \`${pathName}\`.`,
|
|
||||||
hint: (possibleRoutes) => `Possible dynamic routes being matched: ${possibleRoutes.join(", ")}.`
|
|
||||||
};
|
|
||||||
var OnlyResponseCanBeReturned = {
|
|
||||||
name: "OnlyResponseCanBeReturned",
|
|
||||||
title: "Invalid type returned by Astro page.",
|
|
||||||
message: (route, returnedValue) => `Route \`${route ? route : ""}\` returned a \`${returnedValue}\`. Only a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) can be returned from Astro files.`,
|
|
||||||
hint: "See https://docs.astro.build/en/guides/on-demand-rendering/#response for more information."
|
|
||||||
};
|
|
||||||
var MissingMediaQueryDirective = {
|
|
||||||
name: "MissingMediaQueryDirective",
|
|
||||||
title: "Missing value for `client:media` directive.",
|
|
||||||
message: "Media query not provided for `client:media` directive. A media query similar to `client:media=\"(max-width: 600px)\"` must be provided."
|
|
||||||
};
|
|
||||||
var NoMatchingRenderer = {
|
|
||||||
name: "NoMatchingRenderer",
|
|
||||||
title: "No matching renderer found.",
|
|
||||||
message: (componentName, componentExtension, plural, validRenderersCount) => `Unable to render \`${componentName}\`.
|
|
||||||
|
|
||||||
${validRenderersCount > 0 ? `There ${plural ? "are" : "is"} ${validRenderersCount} renderer${plural ? "s" : ""} configured in your \`astro.config.mjs\` file,
|
|
||||||
but ${plural ? "none were" : "it was not"} able to server-side render \`${componentName}\`.` : `No valid renderer was found ${componentExtension ? `for the \`.${componentExtension}\` file extension.` : `for this file extension.`}`}`,
|
|
||||||
hint: (probableRenderers) => `Did you mean to enable the ${probableRenderers} integration?
|
|
||||||
|
|
||||||
See https://docs.astro.build/en/guides/framework-components/ for more information on how to install and configure integrations.`
|
|
||||||
};
|
|
||||||
var NoClientOnlyHint = {
|
|
||||||
name: "NoClientOnlyHint",
|
|
||||||
title: "Missing hint on client:only directive.",
|
|
||||||
message: (componentName) => `Unable to render \`${componentName}\`. When using the \`client:only\` hydration strategy, Astro needs a hint to use the correct renderer.`,
|
|
||||||
hint: (probableRenderers) => `Did you mean to pass \`client:only="${probableRenderers}"\`? See https://docs.astro.build/en/reference/directives-reference/#clientonly for more information on \`client:only\`.`
|
|
||||||
};
|
|
||||||
var InvalidGetStaticPathsEntry = {
|
|
||||||
name: "InvalidGetStaticPathsEntry",
|
|
||||||
title: "Invalid entry inside `getStaticPaths()`'s return value.",
|
|
||||||
message: (entryType) => `Invalid entry returned by \`getStaticPaths()\`. Expected an object, got \`${entryType}\`.`,
|
|
||||||
hint: "If you're using a `.map` call, you might be looking for `.flatMap()` instead. See https://docs.astro.build/en/reference/routing-reference/#getstaticpaths for more information on `getStaticPaths()`."
|
|
||||||
};
|
|
||||||
var InvalidGetStaticPathsReturn = {
|
|
||||||
name: "InvalidGetStaticPathsReturn",
|
|
||||||
title: "Invalid value returned by `getStaticPaths()`.",
|
|
||||||
message: (returnType) => `Invalid type returned by \`getStaticPaths()\`. Expected an \`array\`, got \`${returnType}\`.`,
|
|
||||||
hint: "See https://docs.astro.build/en/reference/routing-reference/#getstaticpaths for more information on `getStaticPaths()`."
|
|
||||||
};
|
|
||||||
var GetStaticPathsExpectedParams = {
|
|
||||||
name: "GetStaticPathsExpectedParams",
|
|
||||||
title: "Missing params property on `getStaticPaths()` route.",
|
|
||||||
message: "Missing or empty required `params` property on `getStaticPaths()` route.",
|
|
||||||
hint: "See https://docs.astro.build/en/reference/routing-reference/#getstaticpaths for more information on `getStaticPaths()`."
|
|
||||||
};
|
|
||||||
var GetStaticPathsInvalidRouteParam = {
|
|
||||||
name: "GetStaticPathsInvalidRouteParam",
|
|
||||||
title: "Invalid route parameter returned by `getStaticPaths()`.",
|
|
||||||
message: (key, value, valueType) => `Invalid \`getStaticPaths()\` route parameter for \`${key}\`. Expected a string or undefined, received \`${valueType}\` (\`${value}\`).`,
|
|
||||||
hint: "See https://docs.astro.build/en/reference/routing-reference/#getstaticpaths for more information on `getStaticPaths()`."
|
|
||||||
};
|
|
||||||
var GetStaticPathsRequired = {
|
|
||||||
name: "GetStaticPathsRequired",
|
|
||||||
title: "`getStaticPaths()` function required for dynamic routes.",
|
|
||||||
message: "`getStaticPaths()` function is required for dynamic routes. Make sure that you `export` a `getStaticPaths()` function from your dynamic route.",
|
|
||||||
hint: `See https://docs.astro.build/en/guides/routing/#dynamic-routes for more information on dynamic routes.
|
|
||||||
|
|
||||||
If you meant for this route to be server-rendered, set \`export const prerender = false;\` in the page.`
|
|
||||||
};
|
|
||||||
var ReservedSlotName = {
|
|
||||||
name: "ReservedSlotName",
|
|
||||||
title: "Invalid slot name.",
|
|
||||||
message: (slotName) => `Unable to create a slot named \`${slotName}\`. \`${slotName}\` is a reserved slot name. Please update the name of this slot.`
|
|
||||||
};
|
|
||||||
var NoMatchingImport = {
|
|
||||||
name: "NoMatchingImport",
|
|
||||||
title: "No import found for component.",
|
|
||||||
message: (componentName) => `Could not render \`${componentName}\`. No matching import has been found for \`${componentName}\`.`,
|
|
||||||
hint: "Please make sure the component is properly imported."
|
|
||||||
};
|
|
||||||
var InvalidComponentArgs = {
|
|
||||||
name: "InvalidComponentArgs",
|
|
||||||
title: "Invalid component arguments.",
|
|
||||||
message: (name) => `Invalid arguments passed to${name ? ` <${name}>` : ""} component.`,
|
|
||||||
hint: "Astro components cannot be rendered directly via function call, such as `Component()` or `{items.map(Component)}`."
|
|
||||||
};
|
|
||||||
var PageNumberParamNotFound = {
|
|
||||||
name: "PageNumberParamNotFound",
|
|
||||||
title: "Page number param not found.",
|
|
||||||
message: (paramName) => `[paginate()] page number param \`${paramName}\` not found in your filepath.`,
|
|
||||||
hint: "Rename your file to `[page].astro` or `[...page].astro`."
|
|
||||||
};
|
|
||||||
var ImageMissingAlt = {
|
|
||||||
name: "ImageMissingAlt",
|
|
||||||
title: "Image missing required \"alt\" property.",
|
|
||||||
message: "Image missing \"alt\" property. \"alt\" text is required to describe important images on the page.",
|
|
||||||
hint: "Use an empty string (\"\") for decorative images."
|
|
||||||
};
|
|
||||||
var InvalidImageService = {
|
|
||||||
name: "InvalidImageService",
|
|
||||||
title: "Error while loading image service.",
|
|
||||||
message: "There was an error loading the configured image service. Please see the stack trace for more information."
|
|
||||||
};
|
|
||||||
var MissingImageDimension = {
|
|
||||||
name: "MissingImageDimension",
|
|
||||||
title: "Missing image dimensions.",
|
|
||||||
message: (missingDimension, imageURL) => `Missing ${missingDimension === "both" ? "width and height attributes" : `${missingDimension} attribute`} for ${imageURL}. When using remote images, both dimensions are required in order to avoid CLS.`,
|
|
||||||
hint: "If your image is inside your `src` folder, you probably meant to import it instead. See [the Imports guide for more information](https://docs.astro.build/en/guides/imports/#other-assets). You can also use `inferSize={true}` for remote images to get the original dimensions."
|
|
||||||
};
|
|
||||||
var FailedToFetchRemoteImageDimensions = {
|
|
||||||
name: "FailedToFetchRemoteImageDimensions",
|
|
||||||
title: "Failed to retrieve remote image dimensions.",
|
|
||||||
message: (imageURL) => `Failed to get the dimensions for ${imageURL}.`,
|
|
||||||
hint: "Verify your remote image URL is accurate, and that you are not using `inferSize` with a file located in your `public/` folder."
|
|
||||||
};
|
|
||||||
var RemoteImageNotAllowed = {
|
|
||||||
name: "RemoteImageNotAllowed",
|
|
||||||
title: "Remote image is not allowed.",
|
|
||||||
message: (imageURL) => `Remote image ${imageURL} is not allowed by your image configuration.`,
|
|
||||||
hint: "Update `image.domains` or `image.remotePatterns`, or remove `inferSize` for this image."
|
|
||||||
};
|
|
||||||
var UnsupportedImageFormat = {
|
|
||||||
name: "UnsupportedImageFormat",
|
|
||||||
title: "Unsupported image format.",
|
|
||||||
message: (format, imagePath, supportedFormats) => `Received unsupported format \`${format}\` from \`${imagePath}\`. Currently only ${supportedFormats.join(", ")} are supported by our image services.`,
|
|
||||||
hint: "Using an `img` tag directly instead of the `Image` component might be what you're looking for."
|
|
||||||
};
|
|
||||||
var UnsupportedImageConversion = {
|
|
||||||
name: "UnsupportedImageConversion",
|
|
||||||
title: "Unsupported image conversion.",
|
|
||||||
message: "Converting between vector (such as SVGs) and raster (such as PNGs and JPEGs) images is not currently supported."
|
|
||||||
};
|
|
||||||
var PrerenderDynamicEndpointPathCollide = {
|
|
||||||
name: "PrerenderDynamicEndpointPathCollide",
|
|
||||||
title: "Prerendered dynamic endpoint has path collision.",
|
|
||||||
message: (pathname) => `Could not render \`${pathname}\` with an \`undefined\` param as the generated path will collide during prerendering. Prevent passing \`undefined\` as \`params\` for the endpoint's \`getStaticPaths()\` function, or add an additional extension to the endpoint's filename.`,
|
|
||||||
hint: (filename) => `Rename \`${filename}\` to \`${filename.replace(/\.(?:js|ts)/, (m) => `.json` + m)}\``
|
|
||||||
};
|
|
||||||
var ExpectedImage = {
|
|
||||||
name: "ExpectedImage",
|
|
||||||
title: "Expected src to be an image.",
|
|
||||||
message: (src, typeofOptions, fullOptions) => `Expected \`src\` property for \`getImage\` or \`<Image />\` to be either an ESM imported image or a string with the path of a remote image. Received \`${src}\` (type: \`${typeofOptions}\`).
|
|
||||||
|
|
||||||
Full serialized options received: \`${fullOptions}\`.`,
|
|
||||||
hint: "This error can often happen because of a wrong path. Make sure the path to your image is correct. If you're passing an async function, make sure to call and await it."
|
|
||||||
};
|
|
||||||
var ExpectedImageOptions = {
|
|
||||||
name: "ExpectedImageOptions",
|
|
||||||
title: "Expected image options.",
|
|
||||||
message: (options) => `Expected \`getImage()\` parameter to be an object. Received \`${options}\`.`
|
|
||||||
};
|
|
||||||
var ExpectedNotESMImage = {
|
|
||||||
name: "ExpectedNotESMImage",
|
|
||||||
title: "Expected image options, not an ESM-imported image.",
|
|
||||||
message: "An ESM-imported image cannot be passed directly to `getImage()`. Instead, pass an object with the image in the `src` property.",
|
|
||||||
hint: "Try changing `getImage(myImage)` to `getImage({ src: myImage })`"
|
|
||||||
};
|
|
||||||
var IncompatibleDescriptorOptions = {
|
|
||||||
name: "IncompatibleDescriptorOptions",
|
|
||||||
title: "Cannot set both `densities` and `widths`.",
|
|
||||||
message: "Only one of `densities` or `widths` can be specified. In most cases, you'll probably want to use only `widths` if you require specific widths.",
|
|
||||||
hint: "Those attributes are used to construct a `srcset` attribute, which cannot have both `x` and `w` descriptors."
|
|
||||||
};
|
|
||||||
var NoImageMetadata = {
|
|
||||||
name: "NoImageMetadata",
|
|
||||||
title: "Could not process image metadata.",
|
|
||||||
message: (imagePath) => `Could not process image metadata${imagePath ? ` for \`${imagePath}\`` : ""}.`,
|
|
||||||
hint: "This is often caused by a corrupted or malformed image. Re-exporting the image from your image editor may fix this issue."
|
|
||||||
};
|
|
||||||
var ResponseSentError = {
|
|
||||||
name: "ResponseSentError",
|
|
||||||
title: "Unable to set response.",
|
|
||||||
message: "The response has already been sent to the browser and cannot be altered."
|
|
||||||
};
|
|
||||||
var MiddlewareNoDataOrNextCalled = {
|
|
||||||
name: "MiddlewareNoDataOrNextCalled",
|
|
||||||
title: "The middleware didn't return a `Response`.",
|
|
||||||
message: "Make sure your middleware returns a `Response` object, either directly or by returning the `Response` from calling the `next` function."
|
|
||||||
};
|
|
||||||
var MiddlewareNotAResponse = {
|
|
||||||
name: "MiddlewareNotAResponse",
|
|
||||||
title: "The middleware returned something that is not a `Response` object.",
|
|
||||||
message: "Any data returned from middleware must be a valid `Response` object."
|
|
||||||
};
|
|
||||||
var EndpointDidNotReturnAResponse = {
|
|
||||||
name: "EndpointDidNotReturnAResponse",
|
|
||||||
title: "The endpoint did not return a `Response`.",
|
|
||||||
message: "An endpoint must return either a `Response`, or a `Promise` that resolves with a `Response`."
|
|
||||||
};
|
|
||||||
var LocalsNotAnObject = {
|
|
||||||
name: "LocalsNotAnObject",
|
|
||||||
title: "Value assigned to `locals` is not accepted.",
|
|
||||||
message: "`locals` can only be assigned to an object. Other values like numbers, strings, etc. are not accepted.",
|
|
||||||
hint: "If you tried to remove some information from the `locals` object, try to use `delete` or set the property to `undefined`."
|
|
||||||
};
|
|
||||||
var LocalsReassigned = {
|
|
||||||
name: "LocalsReassigned",
|
|
||||||
title: "`locals` must not be reassigned.",
|
|
||||||
message: "`locals` cannot be assigned directly.",
|
|
||||||
hint: "Set a `locals` property instead."
|
|
||||||
};
|
|
||||||
var AstroResponseHeadersReassigned = {
|
|
||||||
name: "AstroResponseHeadersReassigned",
|
|
||||||
title: "`Astro.response.headers` must not be reassigned.",
|
|
||||||
message: "Individual headers can be added to and removed from `Astro.response.headers`, but it must not be replaced with another instance of `Headers` altogether.",
|
|
||||||
hint: "Consider using `Astro.response.headers.add()`, and `Astro.response.headers.delete()`."
|
|
||||||
};
|
|
||||||
var LocalImageUsedWrongly = {
|
|
||||||
name: "LocalImageUsedWrongly",
|
|
||||||
title: "Local images must be imported.",
|
|
||||||
message: (imageFilePath) => `\`Image\`'s and \`getImage\`'s \`src\` parameter must be an imported image or a URL, it cannot be a string filepath. Received \`${imageFilePath}\`.`,
|
|
||||||
hint: "If you want to use an image from your `src` folder, you need to either import it or if the image is coming from a content collection, use the [image() schema helper](https://docs.astro.build/en/guides/images/#images-in-content-collections). See https://docs.astro.build/en/reference/modules/astro-assets/#src-required for more information on the `src` property."
|
|
||||||
};
|
|
||||||
var MissingSharp = {
|
|
||||||
name: "MissingSharp",
|
|
||||||
title: "Could not find Sharp.",
|
|
||||||
message: "Could not find Sharp. Please install Sharp (`sharp`) manually into your project or migrate to another image service.",
|
|
||||||
hint: "See Sharp's installation instructions for more information: https://sharp.pixelplumbing.com/install. If you are not relying on `astro:assets` to optimize, transform, or process any images, you can configure a passthrough image service instead of installing Sharp. See https://docs.astro.build/en/reference/errors/missing-sharp for more information.\n\nSee https://docs.astro.build/en/guides/images/#default-image-service for more information on how to migrate to another image service."
|
|
||||||
};
|
|
||||||
var i18nNoLocaleFoundInPath = {
|
|
||||||
name: "i18nNoLocaleFoundInPath",
|
|
||||||
title: "The path doesn't contain any locale.",
|
|
||||||
message: "You tried to use an i18n utility on a path that doesn't contain any locale. You can use `pathHasLocale` first to determine if the path has a locale."
|
|
||||||
};
|
|
||||||
var RewriteWithBodyUsed = {
|
|
||||||
name: "RewriteWithBodyUsed",
|
|
||||||
title: "Cannot use `Astro.rewrite()` after the request body has been read.",
|
|
||||||
message: "`Astro.rewrite()` cannot be used if the request body has already been read. If you need to read the body, first clone the request."
|
|
||||||
};
|
|
||||||
var ForbiddenRewrite = {
|
|
||||||
name: "ForbiddenRewrite",
|
|
||||||
title: "Forbidden rewrite to a static route.",
|
|
||||||
message: (from, to, component) => `You tried to rewrite the on-demand route '${from}' with the static route '${to}', when using the 'server' output.
|
|
||||||
|
|
||||||
The static route '${to}' is rendered by the component
|
|
||||||
'${component}', which is marked as prerendered. This is a forbidden operation because during the build, the component '${component}' is compiled to an
|
|
||||||
HTML file, which can't be retrieved at runtime by Astro.`,
|
|
||||||
hint: (component) => `Add \`export const prerender = false\` to the component '${component}', or use \`Astro.redirect()\`.`
|
|
||||||
};
|
|
||||||
var FontFamilyNotFound = {
|
|
||||||
name: "FontFamilyNotFound",
|
|
||||||
title: "Font family not found.",
|
|
||||||
message: (family) => `No data was found for the \`"${family}"\` family passed to the \`<Font>\` component.`,
|
|
||||||
hint: "This is often caused by a typo. Check that the `<Font />` component is using a `cssVariable` specified in your config."
|
|
||||||
};
|
|
||||||
var FontFileUrlNotFound = {
|
|
||||||
name: "FontFileUrlNotFound",
|
|
||||||
title: "Font file URL not found.",
|
|
||||||
message: (url) => `The \`"${url}"\` URL passed to the \`experimental_getFontFileURL()\` function is invalid.`,
|
|
||||||
hint: "Make sure you pass a valid URL, obtained via the `fontData` object."
|
|
||||||
};
|
|
||||||
var MissingGetFontFileRequestUrl = {
|
|
||||||
name: "MissingGetFontFileRequestUrl",
|
|
||||||
title: "`experimental_getFontFileURL()` requires the request URL with on-demand rendering.",
|
|
||||||
hint: "Pass the request URL as the 2nd argument, for example `Astro.url`."
|
|
||||||
};
|
|
||||||
var UnavailableAstroGlobal = {
|
|
||||||
name: "UnavailableAstroGlobal",
|
|
||||||
title: "Unavailable Astro global in `getStaticPaths()`.",
|
|
||||||
message: (name) => `The Astro global is not available in this scope. Please remove \`Astro.${name}\` from your \`getStaticPaths()\` function.`
|
|
||||||
};
|
|
||||||
var NoManifestAvailable = {
|
|
||||||
name: "NoManifestAvailableError",
|
|
||||||
title: "No manifest available.",
|
|
||||||
message: "`new FetchState(request)` was called outside of an Astro server, so no manifest is available.",
|
|
||||||
hint: "Make sure this code runs as part of your Astro app, such as its fetch entrypoint. If this error occurred inside an Astro-built server, please open an issue at https://github.com/withastro/astro/issues."
|
|
||||||
};
|
|
||||||
var ActionsReturnedInvalidDataError = {
|
|
||||||
name: "ActionsReturnedInvalidDataError",
|
|
||||||
title: "Action handler returned invalid data.",
|
|
||||||
message: (error) => `Action handler returned invalid data. Handlers should return serializable data types like objects, arrays, strings, and numbers. Parse error: ${error}`,
|
|
||||||
hint: "See the devalue library for all supported types: https://github.com/rich-harris/devalue"
|
|
||||||
};
|
|
||||||
var ActionNotFoundError = {
|
|
||||||
name: "ActionNotFoundError",
|
|
||||||
title: "Action not found.",
|
|
||||||
message: (actionName) => `The server received a request for an action named \`${actionName}\` but could not find a match. If you renamed an action, check that you've updated your \`actions/index\` file and your calling code to match.`,
|
|
||||||
hint: "You can run `astro check` to detect type errors caused by mismatched action names."
|
|
||||||
};
|
|
||||||
var CacheNotEnabled = {
|
|
||||||
name: "CacheNotEnabled",
|
|
||||||
title: "Cache is not enabled.",
|
|
||||||
message: "`Astro.cache` is not available because the cache feature is not enabled. To use caching, configure a cache provider in your Astro config under `cache`.",
|
|
||||||
hint: "Use an adapter that provides a default cache provider, or set one explicitly: `cache: { provider: \"...\" }`. See https://docs.astro.build/en/guides/caching/."
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
export { isAstroError as $, MissingMediaQueryDirective as A, PrerenderClientAddressNotAvailable as B, LocalImageUsedWrongly as C, MiddlewareNotAResponse as D, MiddlewareNoDataOrNextCalled as E, NoMatchingImport as F, RewriteWithBodyUsed as G, RemoteImageNotAllowed as H, NoMatchingRenderer as I, UnsupportedImageConversion as J, StaticClientAddressNotAvailable as K, NoMatchingStaticPathFound as L, NoClientOnlyHint as M, NoImageMetadata as N, MissingGetFontFileRequestUrl as O, NoManifestAvailable as P, AstroUserError as Q, OnlyResponseCanBeReturned as R, InvalidImageService as S, LocalsReassigned as T, ReservedSlotName as U, PrerenderDynamicEndpointPathCollide as V, ResponseSentError as W, i18nNoLocaleFoundInPath as X, UnsupportedImageFormat as Y, AstroError as Z, ImageMissingAlt as _, ClientAddressNotAvailable as a, InvalidGetStaticPathsEntry as b, ExpectedImageOptions as c, FontFamilyNotFound as d, FontFileUrlNotFound as f, GetStaticPathsRequired as g, GetStaticPathsInvalidRouteParam as h, CacheNotEnabled as i, MissingSharp as j, MissingImageDimension as k, ExpectedNotESMImage as l, GetStaticPathsExpectedParams as m, ActionsReturnedInvalidDataError as n, EndpointDidNotReturnAResponse as o, ForbiddenRewrite as p, UnavailableAstroGlobal as q, AstroResponseHeadersReassigned as r, ExpectedImage as s, ActionNotFoundError as t, FailedToFetchRemoteImageDimensions as u, IncompatibleDescriptorOptions as v, LocalsNotAnObject as w, InvalidGetStaticPathsReturn as x, InvalidComponentArgs as y, PageNumberParamNotFound as z };
|
|
||||||
|
|
@ -1,720 +0,0 @@
|
||||||
import { n as __exportAll, t as createComponent } from "./compiler_DiXpMM4i.mjs";
|
|
||||||
import { C as LocalImageUsedWrongly, H as RemoteImageNotAllowed, J as UnsupportedImageConversion, O as MissingGetFontFileRequestUrl, S as InvalidImageService, Y as UnsupportedImageFormat, Z as AstroError, _ as ImageMissingAlt, c as ExpectedImageOptions, d as FontFamilyNotFound, k as MissingImageDimension, l as ExpectedNotESMImage, s as ExpectedImage, v as IncompatibleDescriptorOptions } from "./errors-data_zYnyWRO_.mjs";
|
|
||||||
import { _ as inferRemoteSize$1, b as isRemoteAllowed, c as isRemotePath, f as removeQueryString, l as joinPaths, v as fetchWithRedirects } from "./path_BQC-SKOp.mjs";
|
|
||||||
import { S as unescapeHTML, _ as addAttribute, h as maybeRenderHead, m as renderTemplate, r as spreadAttributes, w as createAstro } from "./jsx-runtime_DseUv9uh.mjs";
|
|
||||||
import * as mime from "mrmime";
|
|
||||||
//#region node_modules/astro/dist/assets/utils/imageKind.js
|
|
||||||
function isESMImportedImage(src) {
|
|
||||||
return typeof src === "object" || typeof src === "function" && "src" in src;
|
|
||||||
}
|
|
||||||
function isRemoteImage(src) {
|
|
||||||
return typeof src === "string";
|
|
||||||
}
|
|
||||||
async function resolveSrc(src) {
|
|
||||||
if (typeof src === "object" && "then" in src) {
|
|
||||||
const resource = await src;
|
|
||||||
return resource.default ?? resource;
|
|
||||||
}
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
var VALID_SUPPORTED_FORMATS = [
|
|
||||||
"jpeg",
|
|
||||||
"jpg",
|
|
||||||
"png",
|
|
||||||
"tiff",
|
|
||||||
"webp",
|
|
||||||
"gif",
|
|
||||||
"svg",
|
|
||||||
"avif"
|
|
||||||
];
|
|
||||||
var DEFAULT_OUTPUT_FORMAT = "webp";
|
|
||||||
var DEFAULT_HASH_PROPS = [
|
|
||||||
"src",
|
|
||||||
"width",
|
|
||||||
"height",
|
|
||||||
"format",
|
|
||||||
"quality",
|
|
||||||
"fit",
|
|
||||||
"position",
|
|
||||||
"background"
|
|
||||||
];
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/layout.js
|
|
||||||
var DEFAULT_RESOLUTIONS = [
|
|
||||||
640,
|
|
||||||
750,
|
|
||||||
828,
|
|
||||||
960,
|
|
||||||
1080,
|
|
||||||
1280,
|
|
||||||
1668,
|
|
||||||
1920,
|
|
||||||
2048,
|
|
||||||
2560,
|
|
||||||
3200,
|
|
||||||
3840,
|
|
||||||
4480,
|
|
||||||
5120,
|
|
||||||
6016
|
|
||||||
];
|
|
||||||
var LIMITED_RESOLUTIONS = [
|
|
||||||
640,
|
|
||||||
750,
|
|
||||||
828,
|
|
||||||
1080,
|
|
||||||
1280,
|
|
||||||
1668,
|
|
||||||
2048,
|
|
||||||
2560
|
|
||||||
];
|
|
||||||
var getWidths = ({ width, layout, breakpoints = DEFAULT_RESOLUTIONS, originalWidth }) => {
|
|
||||||
const smallerThanOriginal = (w) => !originalWidth || w <= originalWidth;
|
|
||||||
if (layout === "full-width") return breakpoints.filter(smallerThanOriginal);
|
|
||||||
if (!width) return [];
|
|
||||||
const doubleWidth = width * 2;
|
|
||||||
const maxSize = originalWidth ? Math.min(doubleWidth, originalWidth) : doubleWidth;
|
|
||||||
if (layout === "fixed") return originalWidth && width > originalWidth ? [originalWidth] : [width, maxSize];
|
|
||||||
if (layout === "constrained") return [
|
|
||||||
width,
|
|
||||||
doubleWidth,
|
|
||||||
...breakpoints
|
|
||||||
].filter((w) => w <= maxSize).sort((a, b) => a - b);
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
var getSizesAttribute = ({ width, layout }) => {
|
|
||||||
if (!width || !layout) return;
|
|
||||||
switch (layout) {
|
|
||||||
case "constrained": return `(min-width: ${width}px) ${width}px, 100vw`;
|
|
||||||
case "fixed": return `${width}px`;
|
|
||||||
case "full-width": return `100vw`;
|
|
||||||
default: return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/utils/inferSourceFormat.js
|
|
||||||
var DATA_PREFIX = "data:";
|
|
||||||
function inferSourceFormat(src) {
|
|
||||||
if (src.startsWith(DATA_PREFIX)) {
|
|
||||||
const sepIndex = src.indexOf(";");
|
|
||||||
const commaIndex = src.indexOf(",");
|
|
||||||
const mimeEnd = sepIndex === -1 ? commaIndex : commaIndex === -1 ? sepIndex : Math.min(sepIndex, commaIndex);
|
|
||||||
if (mimeEnd === -1) return void 0;
|
|
||||||
const mime = src.slice(5, mimeEnd);
|
|
||||||
if (mime === "image/svg+xml") return "svg";
|
|
||||||
return mime.split("/")[1] || void 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const cleanSrc = removeQueryString(src).split("#")[0];
|
|
||||||
const lastSlash = cleanSrc.lastIndexOf("/");
|
|
||||||
const basename = lastSlash === -1 ? cleanSrc : cleanSrc.slice(lastSlash + 1);
|
|
||||||
const lastDot = basename.lastIndexOf(".");
|
|
||||||
if (lastDot === -1) return void 0;
|
|
||||||
return basename.slice(lastDot + 1).toLowerCase();
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function resolveDefaultOutputFormat(sourceFormat) {
|
|
||||||
return sourceFormat === "svg" ? "svg" : DEFAULT_OUTPUT_FORMAT;
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/services/service.js
|
|
||||||
function isLocalService(service) {
|
|
||||||
if (!service) return false;
|
|
||||||
return "transform" in service;
|
|
||||||
}
|
|
||||||
function parseQuality(quality) {
|
|
||||||
let result = Number.parseInt(quality);
|
|
||||||
if (Number.isNaN(result)) return quality;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
var sortNumeric = (a, b) => a - b;
|
|
||||||
function verifyOptions(options) {
|
|
||||||
if (!options.src || !isRemoteImage(options.src) && !isESMImportedImage(options.src)) throw new AstroError({
|
|
||||||
...ExpectedImage,
|
|
||||||
message: ExpectedImage.message(JSON.stringify(options.src), typeof options.src, JSON.stringify(options, (_, v) => v === void 0 ? null : v))
|
|
||||||
});
|
|
||||||
if (!isESMImportedImage(options.src)) {
|
|
||||||
if (options.src.startsWith("/@fs/") || !isRemotePath(options.src) && !options.src.startsWith("/")) throw new AstroError({
|
|
||||||
...LocalImageUsedWrongly,
|
|
||||||
message: LocalImageUsedWrongly.message(options.src)
|
|
||||||
});
|
|
||||||
let missingDimension;
|
|
||||||
if (!options.width && !options.height) missingDimension = "both";
|
|
||||||
else if (!options.width && options.height) missingDimension = "width";
|
|
||||||
else if (options.width && !options.height) missingDimension = "height";
|
|
||||||
if (missingDimension) throw new AstroError({
|
|
||||||
...MissingImageDimension,
|
|
||||||
message: MissingImageDimension.message(missingDimension, options.src)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (!VALID_SUPPORTED_FORMATS.includes(options.src.format)) throw new AstroError({
|
|
||||||
...UnsupportedImageFormat,
|
|
||||||
message: UnsupportedImageFormat.message(options.src.format, options.src.src, VALID_SUPPORTED_FORMATS)
|
|
||||||
});
|
|
||||||
if (options.widths && options.densities) throw new AstroError(IncompatibleDescriptorOptions);
|
|
||||||
if (options.src.format !== "svg" && options.format === "svg") throw new AstroError(UnsupportedImageConversion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var baseService = {
|
|
||||||
propertiesToHash: DEFAULT_HASH_PROPS,
|
|
||||||
validateOptions(options) {
|
|
||||||
verifyOptions(options);
|
|
||||||
if (!options.format) {
|
|
||||||
if (isESMImportedImage(options.src)) options.format = resolveDefaultOutputFormat(options.src.format);
|
|
||||||
else {
|
|
||||||
const inferred = inferSourceFormat(options.src);
|
|
||||||
if (inferred) options.format = resolveDefaultOutputFormat(inferred);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (options.width) options.width = Math.round(options.width);
|
|
||||||
if (options.height) options.height = Math.round(options.height);
|
|
||||||
if (options.layout) delete options.layout;
|
|
||||||
if (options.fit === "none") delete options.fit;
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
getHTMLAttributes(options) {
|
|
||||||
const { targetWidth, targetHeight } = getTargetDimensions(options);
|
|
||||||
const { src, width, height, format, quality, densities, widths, formats, layout, priority, fit, position, background, ...attributes } = options;
|
|
||||||
return {
|
|
||||||
...attributes,
|
|
||||||
width: targetWidth,
|
|
||||||
height: targetHeight,
|
|
||||||
loading: attributes.loading ?? "lazy",
|
|
||||||
decoding: attributes.decoding ?? "async"
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getSrcSet(options) {
|
|
||||||
const { targetWidth, targetHeight } = getTargetDimensions(options);
|
|
||||||
const aspectRatio = targetWidth / targetHeight;
|
|
||||||
const { widths, densities } = options;
|
|
||||||
const targetFormat = options.format;
|
|
||||||
let transformedWidths = (widths ?? []).sort(sortNumeric);
|
|
||||||
let imageWidth = options.width;
|
|
||||||
let maxWidth = Number.POSITIVE_INFINITY;
|
|
||||||
if (isESMImportedImage(options.src)) {
|
|
||||||
imageWidth = options.src.width;
|
|
||||||
maxWidth = imageWidth;
|
|
||||||
if (transformedWidths.length > 0 && transformedWidths.at(-1) > maxWidth) {
|
|
||||||
transformedWidths = transformedWidths.filter((width) => width <= maxWidth);
|
|
||||||
transformedWidths.push(maxWidth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
transformedWidths = Array.from(new Set(transformedWidths));
|
|
||||||
const { width: transformWidth, height: transformHeight, ...transformWithoutDimensions } = options;
|
|
||||||
let allWidths = [];
|
|
||||||
if (densities) {
|
|
||||||
const densityValues = densities.map((density) => {
|
|
||||||
if (typeof density === "number") return density;
|
|
||||||
else return Number.parseFloat(density);
|
|
||||||
});
|
|
||||||
allWidths = densityValues.sort(sortNumeric).map((density) => Math.round(targetWidth * density)).map((width, index) => ({
|
|
||||||
width,
|
|
||||||
descriptor: `${densityValues[index]}x`
|
|
||||||
}));
|
|
||||||
} else if (transformedWidths.length > 0) allWidths = transformedWidths.map((width) => ({
|
|
||||||
width,
|
|
||||||
descriptor: `${width}w`
|
|
||||||
}));
|
|
||||||
return allWidths.map(({ width, descriptor }) => {
|
|
||||||
const height = Math.round(width / aspectRatio);
|
|
||||||
return {
|
|
||||||
transform: {
|
|
||||||
...transformWithoutDimensions,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
},
|
|
||||||
descriptor,
|
|
||||||
attributes: targetFormat ? { type: `image/${targetFormat}` } : {}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getURL(options, imageConfig) {
|
|
||||||
const searchParams = new URLSearchParams();
|
|
||||||
if (isESMImportedImage(options.src)) searchParams.append("href", options.src.src);
|
|
||||||
else if (isRemoteAllowed(options.src, imageConfig)) searchParams.append("href", options.src);
|
|
||||||
else return options.src;
|
|
||||||
Object.entries({
|
|
||||||
w: "width",
|
|
||||||
h: "height",
|
|
||||||
q: "quality",
|
|
||||||
f: "format",
|
|
||||||
fit: "fit",
|
|
||||||
position: "position",
|
|
||||||
background: "background"
|
|
||||||
}).forEach(([param, key]) => {
|
|
||||||
options[key] && searchParams.append(param, options[key].toString());
|
|
||||||
});
|
|
||||||
let url = `${joinPaths("/", imageConfig.endpoint.route)}?${searchParams}`;
|
|
||||||
if (imageConfig.assetQueryParams) {
|
|
||||||
const assetQueryString = imageConfig.assetQueryParams.toString();
|
|
||||||
if (assetQueryString) url += "&" + assetQueryString;
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
},
|
|
||||||
parseURL(url) {
|
|
||||||
const params = url.searchParams;
|
|
||||||
if (!params.has("href")) return;
|
|
||||||
return {
|
|
||||||
src: params.get("href"),
|
|
||||||
width: params.has("w") ? Number.parseInt(params.get("w")) : void 0,
|
|
||||||
height: params.has("h") ? Number.parseInt(params.get("h")) : void 0,
|
|
||||||
format: params.has("f") ? params.get("f") : void 0,
|
|
||||||
quality: params.get("q"),
|
|
||||||
fit: params.get("fit"),
|
|
||||||
position: params.get("position") ?? void 0,
|
|
||||||
background: params.get("background") ?? void 0
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getRemoteSize(url, imageConfig) {
|
|
||||||
return inferRemoteSize$1(url, imageConfig);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
function getTargetDimensions(options) {
|
|
||||||
let targetWidth = options.width;
|
|
||||||
let targetHeight = options.height;
|
|
||||||
if (isESMImportedImage(options.src)) {
|
|
||||||
const aspectRatio = options.src.width / options.src.height;
|
|
||||||
if (targetHeight && !targetWidth) targetWidth = Math.round(targetHeight * aspectRatio);
|
|
||||||
else if (targetWidth && !targetHeight) targetHeight = Math.round(targetWidth / aspectRatio);
|
|
||||||
else if (!targetWidth && !targetHeight) {
|
|
||||||
targetWidth = options.src.width;
|
|
||||||
targetHeight = options.src.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
targetWidth,
|
|
||||||
targetHeight
|
|
||||||
};
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/types.js
|
|
||||||
function isImageMetadata(src) {
|
|
||||||
return src.fsPath && !("fsPath" in src);
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/utils/url.js
|
|
||||||
var PLACEHOLDER_BASE = "astro://placeholder";
|
|
||||||
function createPlaceholderURL(pathOrUrl) {
|
|
||||||
return new URL(pathOrUrl, PLACEHOLDER_BASE);
|
|
||||||
}
|
|
||||||
function stringifyPlaceholderURL(url) {
|
|
||||||
return url.href.replace(PLACEHOLDER_BASE, "");
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/internal.js
|
|
||||||
var cssFitValues = [
|
|
||||||
"fill",
|
|
||||||
"contain",
|
|
||||||
"cover",
|
|
||||||
"scale-down"
|
|
||||||
];
|
|
||||||
async function getConfiguredImageService() {
|
|
||||||
if (!globalThis?.astroAsset?.imageService) {
|
|
||||||
const { default: service } = await import("./sharp_BQ57rSWH.mjs").catch((e) => {
|
|
||||||
const error = new AstroError(InvalidImageService);
|
|
||||||
error.cause = e;
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
if (!globalThis.astroAsset) globalThis.astroAsset = {};
|
|
||||||
globalThis.astroAsset.imageService = service;
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
return globalThis.astroAsset.imageService;
|
|
||||||
}
|
|
||||||
async function getImage$1(options, imageConfig) {
|
|
||||||
if (!options || typeof options !== "object") throw new AstroError({
|
|
||||||
...ExpectedImageOptions,
|
|
||||||
message: ExpectedImageOptions.message(JSON.stringify(options))
|
|
||||||
});
|
|
||||||
if (typeof options.src === "undefined") throw new AstroError({
|
|
||||||
...ExpectedImage,
|
|
||||||
message: ExpectedImage.message(options.src, "undefined", JSON.stringify(options))
|
|
||||||
});
|
|
||||||
if (isImageMetadata(options)) throw new AstroError(ExpectedNotESMImage);
|
|
||||||
const service = await getConfiguredImageService();
|
|
||||||
const resolvedOptions = {
|
|
||||||
...options,
|
|
||||||
src: await resolveSrc(options.src)
|
|
||||||
};
|
|
||||||
let originalWidth;
|
|
||||||
let originalHeight;
|
|
||||||
if (resolvedOptions.inferSize) {
|
|
||||||
delete resolvedOptions.inferSize;
|
|
||||||
if (isRemoteImage(resolvedOptions.src) && isRemotePath(resolvedOptions.src)) {
|
|
||||||
if (!isRemoteAllowed(resolvedOptions.src, imageConfig)) throw new AstroError({
|
|
||||||
...RemoteImageNotAllowed,
|
|
||||||
message: RemoteImageNotAllowed.message(resolvedOptions.src)
|
|
||||||
});
|
|
||||||
const getRemoteSize = (url) => service.getRemoteSize?.(url, imageConfig) ?? inferRemoteSize$1(url, imageConfig);
|
|
||||||
const result = await getRemoteSize(resolvedOptions.src);
|
|
||||||
resolvedOptions.width ??= result.width;
|
|
||||||
resolvedOptions.height ??= result.height;
|
|
||||||
if (result.format) resolvedOptions.format ??= resolveDefaultOutputFormat(result.format);
|
|
||||||
originalWidth = result.width;
|
|
||||||
originalHeight = result.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const originalFilePath = isESMImportedImage(resolvedOptions.src) ? resolvedOptions.src.fsPath : void 0;
|
|
||||||
const clonedSrc = isESMImportedImage(resolvedOptions.src) ? resolvedOptions.src.clone ?? resolvedOptions.src : resolvedOptions.src;
|
|
||||||
if (isESMImportedImage(clonedSrc)) {
|
|
||||||
originalWidth = clonedSrc.width;
|
|
||||||
originalHeight = clonedSrc.height;
|
|
||||||
}
|
|
||||||
if (originalWidth && originalHeight) {
|
|
||||||
const aspectRatio = originalWidth / originalHeight;
|
|
||||||
if (resolvedOptions.height && !resolvedOptions.width) resolvedOptions.width = Math.round(resolvedOptions.height * aspectRatio);
|
|
||||||
else if (resolvedOptions.width && !resolvedOptions.height) resolvedOptions.height = Math.round(resolvedOptions.width / aspectRatio);
|
|
||||||
else if (!resolvedOptions.width && !resolvedOptions.height) {
|
|
||||||
resolvedOptions.width = originalWidth;
|
|
||||||
resolvedOptions.height = originalHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolvedOptions.src = clonedSrc;
|
|
||||||
const layout = options.layout ?? imageConfig.layout ?? "none";
|
|
||||||
if (resolvedOptions.priority) {
|
|
||||||
resolvedOptions.loading ??= "eager";
|
|
||||||
resolvedOptions.decoding ??= "sync";
|
|
||||||
resolvedOptions.fetchpriority ??= "high";
|
|
||||||
delete resolvedOptions.priority;
|
|
||||||
} else {
|
|
||||||
resolvedOptions.loading ??= "lazy";
|
|
||||||
resolvedOptions.decoding ??= "async";
|
|
||||||
resolvedOptions.fetchpriority ??= void 0;
|
|
||||||
}
|
|
||||||
if (layout !== "none") {
|
|
||||||
resolvedOptions.widths ||= getWidths({
|
|
||||||
width: resolvedOptions.width,
|
|
||||||
layout,
|
|
||||||
originalWidth,
|
|
||||||
breakpoints: imageConfig.breakpoints?.length ? imageConfig.breakpoints : isLocalService(service) ? LIMITED_RESOLUTIONS : DEFAULT_RESOLUTIONS
|
|
||||||
});
|
|
||||||
resolvedOptions.sizes ||= getSizesAttribute({
|
|
||||||
width: resolvedOptions.width,
|
|
||||||
layout
|
|
||||||
});
|
|
||||||
delete resolvedOptions.densities;
|
|
||||||
resolvedOptions["data-astro-image"] = layout;
|
|
||||||
if (resolvedOptions.fit && cssFitValues.includes(resolvedOptions.fit)) resolvedOptions["data-astro-image-fit"] = resolvedOptions.fit;
|
|
||||||
resolvedOptions["data-astro-image-pos"] = (resolvedOptions.position || "center").replace(/\s+/g, "-");
|
|
||||||
}
|
|
||||||
const validatedOptions = service.validateOptions ? await service.validateOptions(resolvedOptions, imageConfig) : resolvedOptions;
|
|
||||||
validatedOptions.format ??= await peekRemoteFormatForStaticEmit(validatedOptions, imageConfig, service);
|
|
||||||
const srcSetTransforms = service.getSrcSet ? await service.getSrcSet(validatedOptions, imageConfig) : [];
|
|
||||||
const lazyImageURLFactory = (getValue) => {
|
|
||||||
let cached = null;
|
|
||||||
return () => cached ??= getValue();
|
|
||||||
};
|
|
||||||
const initialImageURL = await service.getURL(validatedOptions, imageConfig);
|
|
||||||
let lazyImageURL = lazyImageURLFactory(() => initialImageURL);
|
|
||||||
const matchesValidatedTransform = (transform) => transform.width === validatedOptions.width && transform.height === validatedOptions.height && transform.format === validatedOptions.format;
|
|
||||||
let srcSets = await Promise.all(srcSetTransforms.map(async (srcSet) => {
|
|
||||||
return {
|
|
||||||
transform: srcSet.transform,
|
|
||||||
url: matchesValidatedTransform(srcSet.transform) ? initialImageURL : await service.getURL(srcSet.transform, imageConfig),
|
|
||||||
descriptor: srcSet.descriptor,
|
|
||||||
attributes: srcSet.attributes
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
if (isLocalService(service) && globalThis.astroAsset.addStaticImage && !(isRemoteImage(validatedOptions.src) && initialImageURL === validatedOptions.src)) {
|
|
||||||
const propsToHash = service.propertiesToHash ?? DEFAULT_HASH_PROPS;
|
|
||||||
lazyImageURL = lazyImageURLFactory(() => globalThis.astroAsset.addStaticImage(validatedOptions, propsToHash, originalFilePath));
|
|
||||||
srcSets = srcSetTransforms.map((srcSet) => {
|
|
||||||
return {
|
|
||||||
transform: srcSet.transform,
|
|
||||||
url: matchesValidatedTransform(srcSet.transform) ? lazyImageURL() : globalThis.astroAsset.addStaticImage(srcSet.transform, propsToHash, originalFilePath),
|
|
||||||
descriptor: srcSet.descriptor,
|
|
||||||
attributes: srcSet.attributes
|
|
||||||
};
|
|
||||||
});
|
|
||||||
} else if (imageConfig.assetQueryParams) {
|
|
||||||
const imageURLObj = createPlaceholderURL(initialImageURL);
|
|
||||||
imageConfig.assetQueryParams.forEach((value, key) => {
|
|
||||||
imageURLObj.searchParams.set(key, value);
|
|
||||||
});
|
|
||||||
lazyImageURL = lazyImageURLFactory(() => stringifyPlaceholderURL(imageURLObj));
|
|
||||||
srcSets = srcSets.map((srcSet) => {
|
|
||||||
const urlObj = createPlaceholderURL(srcSet.url);
|
|
||||||
imageConfig.assetQueryParams.forEach((value, key) => {
|
|
||||||
urlObj.searchParams.set(key, value);
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
...srcSet,
|
|
||||||
url: stringifyPlaceholderURL(urlObj)
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
rawOptions: resolvedOptions,
|
|
||||||
options: validatedOptions,
|
|
||||||
get src() {
|
|
||||||
return lazyImageURL();
|
|
||||||
},
|
|
||||||
srcSet: {
|
|
||||||
values: srcSets,
|
|
||||||
attribute: srcSets.map((srcSet) => `${srcSet.url} ${srcSet.descriptor}`).join(", ")
|
|
||||||
},
|
|
||||||
attributes: service.getHTMLAttributes !== void 0 ? await service.getHTMLAttributes(validatedOptions, imageConfig) : {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
async function peekRemoteFormatForStaticEmit(options, imageConfig, service) {
|
|
||||||
if (!isRemoteImage(options.src) || !isRemoteAllowed(options.src, imageConfig) || !globalThis.astroAsset?.addStaticImage || !isLocalService(service) || !service.getRemoteSize) return;
|
|
||||||
try {
|
|
||||||
return resolveDefaultOutputFormat((await service.getRemoteSize(options.src, imageConfig)).format);
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Function.prototype.toString.call(Object);
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/components/Image.astro
|
|
||||||
createAstro("https://gabeefran.co");
|
|
||||||
var $$Image = createComponent(async ($$result, $$props, $$slots) => {
|
|
||||||
const Astro2 = $$result.createAstro($$props, $$slots);
|
|
||||||
Astro2.self = $$Image;
|
|
||||||
const props = Astro2.props;
|
|
||||||
if (props.alt === void 0 || props.alt === null) throw new AstroError(ImageMissingAlt);
|
|
||||||
if (typeof props.width === "string") props.width = Number.parseInt(props.width);
|
|
||||||
if (typeof props.height === "string") props.height = Number.parseInt(props.height);
|
|
||||||
if ((props.layout ?? imageConfig.layout ?? "none") !== "none") {
|
|
||||||
props.layout ??= imageConfig.layout;
|
|
||||||
props.fit ??= imageConfig.objectFit ?? "cover";
|
|
||||||
props.position ??= imageConfig.objectPosition ?? "center";
|
|
||||||
} else if (imageConfig.objectFit || imageConfig.objectPosition) {
|
|
||||||
props.fit ??= imageConfig.objectFit;
|
|
||||||
props.position ??= imageConfig.objectPosition;
|
|
||||||
}
|
|
||||||
const image = await getImage(props);
|
|
||||||
const additionalAttributes = {};
|
|
||||||
if (image.srcSet.values.length > 0) additionalAttributes.srcset = image.srcSet.attribute;
|
|
||||||
const { class: className, ...attributes } = {
|
|
||||||
...additionalAttributes,
|
|
||||||
...image.attributes
|
|
||||||
};
|
|
||||||
return renderTemplate`${maybeRenderHead($$result)}<img${addAttribute(image.src, "src")}${spreadAttributes(attributes)}${addAttribute(className, "class")}>`;
|
|
||||||
}, "/home/gabe/src/gabeefran.co-blog/node_modules/astro/components/Image.astro", void 0);
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/components/Picture.astro
|
|
||||||
createAstro("https://gabeefran.co");
|
|
||||||
var $$Picture = createComponent(async ($$result, $$props, $$slots) => {
|
|
||||||
const Astro2 = $$result.createAstro($$props, $$slots);
|
|
||||||
Astro2.self = $$Picture;
|
|
||||||
const defaultFormats = ["webp"];
|
|
||||||
const defaultFallbackFormat = "png";
|
|
||||||
const specialFormatsFallback = [
|
|
||||||
"gif",
|
|
||||||
"svg",
|
|
||||||
"jpg",
|
|
||||||
"jpeg"
|
|
||||||
];
|
|
||||||
const { formats = defaultFormats, pictureAttributes = {}, fallbackFormat, ...props } = Astro2.props;
|
|
||||||
if (props.alt === void 0 || props.alt === null) throw new AstroError(ImageMissingAlt);
|
|
||||||
const scopedStyleClass = props.class?.match(/\bastro-\w{8}\b/)?.[0];
|
|
||||||
if (scopedStyleClass) {
|
|
||||||
if (pictureAttributes.class) pictureAttributes.class = `${pictureAttributes.class} ${scopedStyleClass}`;
|
|
||||||
else pictureAttributes.class = scopedStyleClass;
|
|
||||||
}
|
|
||||||
const useResponsive = (props.layout ?? imageConfig.layout ?? "none") !== "none";
|
|
||||||
if (useResponsive) {
|
|
||||||
props.layout ??= imageConfig.layout;
|
|
||||||
props.fit ??= imageConfig.objectFit ?? "cover";
|
|
||||||
props.position ??= imageConfig.objectPosition ?? "center";
|
|
||||||
} else if (imageConfig.objectFit || imageConfig.objectPosition) {
|
|
||||||
props.fit ??= imageConfig.objectFit;
|
|
||||||
props.position ??= imageConfig.objectPosition;
|
|
||||||
}
|
|
||||||
for (const key in props) if (key.startsWith("data-astro-cid")) pictureAttributes[key] = props[key];
|
|
||||||
const originalSrc = await resolveSrc(props.src);
|
|
||||||
if (props.inferSize && isRemoteImage(originalSrc)) {
|
|
||||||
const remoteSize = await inferRemoteSize(originalSrc);
|
|
||||||
delete props.inferSize;
|
|
||||||
props.width ??= remoteSize.width;
|
|
||||||
props.height ??= remoteSize.height;
|
|
||||||
}
|
|
||||||
const optimizedImages = await Promise.all(formats.map(async (format) => await getImage({
|
|
||||||
...props,
|
|
||||||
src: originalSrc,
|
|
||||||
format,
|
|
||||||
widths: props.widths,
|
|
||||||
densities: props.densities
|
|
||||||
})));
|
|
||||||
const clonedSrc = isESMImportedImage(originalSrc) ? originalSrc.clone ?? originalSrc : originalSrc;
|
|
||||||
let resultFallbackFormat = fallbackFormat ?? defaultFallbackFormat;
|
|
||||||
if (!fallbackFormat && isESMImportedImage(clonedSrc) && specialFormatsFallback.includes(clonedSrc.format)) resultFallbackFormat = clonedSrc.format;
|
|
||||||
const fallbackImage = await getImage({
|
|
||||||
...props,
|
|
||||||
format: resultFallbackFormat,
|
|
||||||
widths: props.widths,
|
|
||||||
densities: props.densities
|
|
||||||
});
|
|
||||||
const imgAdditionalAttributes = {};
|
|
||||||
const sourceAdditionalAttributes = {};
|
|
||||||
if (props.sizes) sourceAdditionalAttributes.sizes = props.sizes;
|
|
||||||
if (fallbackImage.srcSet.values.length > 0) imgAdditionalAttributes.srcset = fallbackImage.srcSet.attribute;
|
|
||||||
const { class: className, ...attributes } = {
|
|
||||||
...imgAdditionalAttributes,
|
|
||||||
...fallbackImage.attributes
|
|
||||||
};
|
|
||||||
return renderTemplate`${maybeRenderHead($$result)}<picture${spreadAttributes(pictureAttributes)}>${Object.entries(optimizedImages).map(([_, image]) => {
|
|
||||||
const srcsetAttribute = props.densities || !props.densities && !props.widths && !useResponsive ? `${image.src}${image.srcSet.values.length > 0 ? ", " + image.srcSet.attribute : ""}` : image.srcSet.attribute;
|
|
||||||
return renderTemplate`<source${addAttribute(srcsetAttribute, "srcset")}${addAttribute(mime.lookup(image.options.format ?? image.src) ?? `image/${image.options.format}`, "type")}${spreadAttributes(sourceAdditionalAttributes)}>`;
|
|
||||||
})}<img${addAttribute(fallbackImage.src, "src")}${spreadAttributes(attributes)}${addAttribute(className, "class")}></picture>`;
|
|
||||||
}, "/home/gabe/src/gabeefran.co-blog/node_modules/astro/components/Picture.astro", void 0);
|
|
||||||
//#endregion
|
|
||||||
//#region \0virtual:astro:assets/fonts/internal
|
|
||||||
var componentDataByCssVariable = /* @__PURE__ */ new Map([]);
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/fonts/core/filter-preloads.js
|
|
||||||
function filterPreloads(data, preload) {
|
|
||||||
if (!preload) return null;
|
|
||||||
if (preload === true) return data;
|
|
||||||
return data.filter(({ weight, style, subset }) => preload.some((p) => {
|
|
||||||
if (p.weight !== void 0 && weight !== void 0 && !checkWeight(p.weight.toString(), weight)) return false;
|
|
||||||
if (p.style !== void 0 && p.style !== style) return false;
|
|
||||||
if (p.subset !== void 0 && p.subset !== subset) return false;
|
|
||||||
return true;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
function checkWeight(input, target) {
|
|
||||||
const trimmedInput = input.trim();
|
|
||||||
if (trimmedInput.includes(" ")) return trimmedInput === target;
|
|
||||||
if (target.includes(" ")) {
|
|
||||||
const [a, b] = target.split(" ");
|
|
||||||
const parsedInput = Number.parseInt(input);
|
|
||||||
return parsedInput >= Number.parseInt(a) && parsedInput <= Number.parseInt(b);
|
|
||||||
}
|
|
||||||
return input === target;
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/components/Font.astro
|
|
||||||
createAstro("https://gabeefran.co");
|
|
||||||
var $$Font = createComponent(($$result, $$props, $$slots) => {
|
|
||||||
const Astro = $$result.createAstro($$props, $$slots);
|
|
||||||
Astro.self = $$Font;
|
|
||||||
const { cssVariable, preload = false } = Astro.props;
|
|
||||||
const data = componentDataByCssVariable.get(cssVariable);
|
|
||||||
if (!data) throw new AstroError({
|
|
||||||
...FontFamilyNotFound,
|
|
||||||
message: FontFamilyNotFound.message(cssVariable)
|
|
||||||
});
|
|
||||||
const filteredPreloadData = filterPreloads(data.preloads, preload);
|
|
||||||
return renderTemplate`<style>${unescapeHTML(data.css)}</style>${filteredPreloadData?.map(({ url, type }) => renderTemplate`<link rel="preload"${addAttribute(url, "href")} as="font"${addAttribute(`font/${type}`, "type")} crossorigin>`)}`;
|
|
||||||
}, "/home/gabe/src/gabeefran.co-blog/node_modules/astro/components/Font.astro", void 0);
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/fonts/infra/ssr-runtime-font-file-url-resolver.js
|
|
||||||
var SsrRuntimeFontFileUrlResolver = class {
|
|
||||||
#urls;
|
|
||||||
constructor({ urls }) {
|
|
||||||
this.#urls = urls;
|
|
||||||
}
|
|
||||||
resolve(url, requestUrl) {
|
|
||||||
if (!this.#urls.has(url)) return null;
|
|
||||||
if (!url.startsWith("/")) return url;
|
|
||||||
if (!requestUrl) throw new AstroError(MissingGetFontFileRequestUrl);
|
|
||||||
return `${requestUrl.origin}${url}`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
new SsrRuntimeFontFileUrlResolver({ urls: /* @__PURE__ */ new Set([]) });
|
|
||||||
//#endregion
|
|
||||||
//#region \0astro:assets
|
|
||||||
var assetQueryParams = void 0;
|
|
||||||
var imageConfig = {
|
|
||||||
"endpoint": { "route": "/_image" },
|
|
||||||
"service": {
|
|
||||||
"entrypoint": "astro/assets/services/sharp",
|
|
||||||
"config": {}
|
|
||||||
},
|
|
||||||
"dangerouslyProcessSVG": false,
|
|
||||||
"domains": [],
|
|
||||||
"remotePatterns": [],
|
|
||||||
"responsiveStyles": false
|
|
||||||
};
|
|
||||||
Object.defineProperty(imageConfig, "assetQueryParams", {
|
|
||||||
value: assetQueryParams,
|
|
||||||
enumerable: false,
|
|
||||||
configurable: true
|
|
||||||
});
|
|
||||||
var inferRemoteSize = async (url) => {
|
|
||||||
return (await getConfiguredImageService()).getRemoteSize?.(url, imageConfig) ?? inferRemoteSize$1(url, imageConfig);
|
|
||||||
};
|
|
||||||
var getImage = async (options) => await getImage$1(options, imageConfig);
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/utils/etag.js
|
|
||||||
var fnv1a52 = (str) => {
|
|
||||||
const len = str.length;
|
|
||||||
let i = 0, t0 = 0, v0 = 8997, t1 = 0, v1 = 33826, t2 = 0, v2 = 40164, t3 = 0, v3 = 52210;
|
|
||||||
while (i < len) {
|
|
||||||
v0 ^= str.charCodeAt(i++);
|
|
||||||
t0 = v0 * 435;
|
|
||||||
t1 = v1 * 435;
|
|
||||||
t2 = v2 * 435;
|
|
||||||
t3 = v3 * 435;
|
|
||||||
t2 += v0 << 8;
|
|
||||||
t3 += v1 << 8;
|
|
||||||
t1 += t0 >>> 16;
|
|
||||||
v0 = t0 & 65535;
|
|
||||||
t2 += t1 >>> 16;
|
|
||||||
v1 = t1 & 65535;
|
|
||||||
v3 = t3 + (t2 >>> 16) & 65535;
|
|
||||||
v2 = t2 & 65535;
|
|
||||||
}
|
|
||||||
return (v3 & 15) * 281474976710656 + v2 * 4294967296 + v1 * 65536 + (v0 ^ v3 >> 4);
|
|
||||||
};
|
|
||||||
var etag = (payload, weak = false) => {
|
|
||||||
return (weak ? "W/\"" : "\"") + fnv1a52(payload).toString(36) + payload.length.toString(36) + "\"";
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/endpoint/loadImage.js
|
|
||||||
async function loadImage(src, headers, imageConfig, isRemote, fetchFn) {
|
|
||||||
try {
|
|
||||||
const res = await fetchWithRedirects({
|
|
||||||
url: src,
|
|
||||||
headers,
|
|
||||||
imageConfig,
|
|
||||||
fetchFn
|
|
||||||
});
|
|
||||||
if (isRemote && !isRemoteAllowed(res.url, imageConfig)) return;
|
|
||||||
if (!res.ok) return;
|
|
||||||
return await res.arrayBuffer();
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/endpoint/generic.js
|
|
||||||
var generic_exports = /* @__PURE__ */ __exportAll({ GET: () => GET });
|
|
||||||
var GET = async ({ request }) => {
|
|
||||||
try {
|
|
||||||
const imageService = await getConfiguredImageService();
|
|
||||||
if (!("transform" in imageService)) throw new Error("Configured image service is not a local service");
|
|
||||||
const url = new URL(request.url);
|
|
||||||
const transform = await imageService.parseURL(url, imageConfig);
|
|
||||||
if (!transform?.src) throw new Error("Incorrect transform returned by `parseURL`");
|
|
||||||
let inputBuffer = void 0;
|
|
||||||
const isRemoteImage = isRemotePath(transform.src);
|
|
||||||
if (isRemoteImage && isRemoteAllowed(transform.src, imageConfig) === false) return new Response("Forbidden", { status: 403 });
|
|
||||||
const sourceUrl = new URL(transform.src, url.origin);
|
|
||||||
if (!isRemoteImage && sourceUrl.origin !== url.origin) return new Response("Forbidden", { status: 403 });
|
|
||||||
inputBuffer = await loadImage(sourceUrl, isRemoteImage ? new Headers() : request.headers, imageConfig, isRemoteImage);
|
|
||||||
if (!inputBuffer) return new Response("Not Found", { status: 404 });
|
|
||||||
const { data, format } = await imageService.transform(new Uint8Array(inputBuffer), transform, imageConfig);
|
|
||||||
return new Response(data, {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": mime.lookup(format) ?? `image/${format}`,
|
|
||||||
"Cache-Control": "public, max-age=31536000",
|
|
||||||
ETag: etag(data.toString()),
|
|
||||||
Date: (/* @__PURE__ */ new Date()).toUTCString()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Could not process image request:", err);
|
|
||||||
return new Response("Internal Server Error", { status: 500 });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
//#region \0virtual:astro:page:node_modules/astro/dist/assets/endpoint/generic@_@js
|
|
||||||
var generic___js_exports = /* @__PURE__ */ __exportAll({ page: () => page });
|
|
||||||
var page = () => generic_exports;
|
|
||||||
//#endregion
|
|
||||||
export { resolveDefaultOutputFormat as i, baseService as n, page, parseQuality as r, generic___js_exports as t };
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
//#region \0virtual:astro:actions/noop-entrypoint
|
|
||||||
var server = {};
|
|
||||||
//#endregion
|
|
||||||
export { server };
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
import { n as __exportAll, t as createComponent } from "./compiler_DiXpMM4i.mjs";
|
|
||||||
import { g as renderHead, m as renderTemplate, w as createAstro } from "./jsx-runtime_DseUv9uh.mjs";
|
|
||||||
//#region src/pages/page-card.astro
|
|
||||||
var page_card_exports = /* @__PURE__ */ __exportAll({
|
|
||||||
default: () => $$PageCard,
|
|
||||||
file: () => $$file,
|
|
||||||
prerender: () => false,
|
|
||||||
url: () => $$url
|
|
||||||
});
|
|
||||||
createAstro("https://gabeefran.co");
|
|
||||||
var $$PageCard = createComponent(($$result, $$props, $$slots) => {
|
|
||||||
const Astro = $$result.createAstro($$props, $$slots);
|
|
||||||
Astro.self = $$PageCard;
|
|
||||||
const title = Astro.url.searchParams.get("title") || "gabeefranco";
|
|
||||||
const description = Astro.url.searchParams.get("description");
|
|
||||||
return renderTemplate`<html data-astro-cid-jwkqgd2l><head><meta charset="utf-8"><meta name="robots" content="noindex"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=JetBrains+Mono:wght@600&display=swap" rel="stylesheet">${renderHead($$result)}</head><body data-astro-cid-jwkqgd2l><div class="brand" data-astro-cid-jwkqgd2l><img src="/logo.png" alt="" data-astro-cid-jwkqgd2l><span data-astro-cid-jwkqgd2l>gabeefranco</span></div><h1 class="title" data-astro-cid-jwkqgd2l>${title}</h1>${description && renderTemplate`<p class="description" data-astro-cid-jwkqgd2l>${description}</p>`}</body></html>`;
|
|
||||||
}, "/home/gabe/src/gabeefran.co-blog/src/pages/page-card.astro", void 0);
|
|
||||||
var $$file = "/home/gabe/src/gabeefran.co-blog/src/pages/page-card.astro";
|
|
||||||
var $$url = "/page-card";
|
|
||||||
//#endregion
|
|
||||||
//#region \0virtual:astro:page:src/pages/page-card@_@astro
|
|
||||||
var page = () => page_card_exports;
|
|
||||||
//#endregion
|
|
||||||
export { page };
|
|
||||||
|
|
@ -1,153 +0,0 @@
|
||||||
import { N as NoImageMetadata, Y as UnsupportedImageFormat, Z as AstroError, j as MissingSharp } from "./errors-data_zYnyWRO_.mjs";
|
|
||||||
import { y as detector } from "./path_BQC-SKOp.mjs";
|
|
||||||
import { i as resolveDefaultOutputFormat, n as baseService, r as parseQuality } from "./generic_DKF9tBMu.mjs";
|
|
||||||
//#region node_modules/astro/dist/assets/services/sharp.js
|
|
||||||
var sharp;
|
|
||||||
var qualityTable = {
|
|
||||||
low: 25,
|
|
||||||
mid: 50,
|
|
||||||
high: 80,
|
|
||||||
max: 100
|
|
||||||
};
|
|
||||||
function resolveSharpQuality(quality) {
|
|
||||||
if (!quality) return void 0;
|
|
||||||
const parsedQuality = parseQuality(quality);
|
|
||||||
if (typeof parsedQuality === "number") return parsedQuality;
|
|
||||||
return quality in qualityTable ? qualityTable[quality] : void 0;
|
|
||||||
}
|
|
||||||
function resolveSharpEncoderOptions(transform, inputFormat, serviceConfig = {}) {
|
|
||||||
const quality = resolveSharpQuality(transform.quality);
|
|
||||||
if (transform.format === void 0) return quality === void 0 ? void 0 : { quality };
|
|
||||||
switch (transform.format) {
|
|
||||||
case "jpg":
|
|
||||||
case "jpeg": return {
|
|
||||||
...serviceConfig.jpeg,
|
|
||||||
...quality === void 0 ? {} : { quality }
|
|
||||||
};
|
|
||||||
case "png": return {
|
|
||||||
...serviceConfig.png,
|
|
||||||
...quality === void 0 ? {} : { quality }
|
|
||||||
};
|
|
||||||
case "webp": {
|
|
||||||
const webpOptions = {
|
|
||||||
...serviceConfig.webp,
|
|
||||||
...quality === void 0 ? {} : { quality }
|
|
||||||
};
|
|
||||||
if (inputFormat === "gif") webpOptions.loop ??= 0;
|
|
||||||
return webpOptions;
|
|
||||||
}
|
|
||||||
case "avif": return {
|
|
||||||
...serviceConfig.avif,
|
|
||||||
...quality === void 0 ? {} : { quality }
|
|
||||||
};
|
|
||||||
default: return quality === void 0 ? void 0 : { quality };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function loadSharp() {
|
|
||||||
let sharpImport;
|
|
||||||
try {
|
|
||||||
sharpImport = (await import("sharp")).default;
|
|
||||||
} catch {
|
|
||||||
throw new AstroError(MissingSharp);
|
|
||||||
}
|
|
||||||
sharpImport.cache(false);
|
|
||||||
return sharpImport;
|
|
||||||
}
|
|
||||||
var fitMap = {
|
|
||||||
fill: "fill",
|
|
||||||
contain: "inside",
|
|
||||||
cover: "cover",
|
|
||||||
none: "outside",
|
|
||||||
"scale-down": "inside",
|
|
||||||
outside: "outside",
|
|
||||||
inside: "inside"
|
|
||||||
};
|
|
||||||
var sharp_default = {
|
|
||||||
validateOptions: baseService.validateOptions,
|
|
||||||
getURL: baseService.getURL,
|
|
||||||
parseURL: baseService.parseURL,
|
|
||||||
getHTMLAttributes: baseService.getHTMLAttributes,
|
|
||||||
getSrcSet: baseService.getSrcSet,
|
|
||||||
getRemoteSize: baseService.getRemoteSize,
|
|
||||||
async transform(inputBuffer, transformOptions, config) {
|
|
||||||
if (!sharp) sharp = await loadSharp();
|
|
||||||
const transform = transformOptions;
|
|
||||||
const kernel = config.service.config.kernel;
|
|
||||||
const bufferFormat = detector(inputBuffer);
|
|
||||||
const outputFormat = transform.format ?? resolveDefaultOutputFormat(bufferFormat);
|
|
||||||
if (outputFormat === "svg") {
|
|
||||||
if (bufferFormat && bufferFormat !== "svg") {
|
|
||||||
console.warn(`\u26A0\uFE0F Astro expected an SVG for "${transform.src}" but the source is ${bufferFormat}. Passing it through as ${bufferFormat} instead.`);
|
|
||||||
return {
|
|
||||||
data: inputBuffer,
|
|
||||||
format: bufferFormat
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
data: inputBuffer,
|
|
||||||
format: "svg"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!bufferFormat) throw new AstroError({
|
|
||||||
...NoImageMetadata,
|
|
||||||
message: NoImageMetadata.message(transform.src)
|
|
||||||
});
|
|
||||||
if (bufferFormat === "svg" && !config.dangerouslyProcessSVG) throw new AstroError({
|
|
||||||
...UnsupportedImageFormat,
|
|
||||||
message: `SVG image processing is disabled, but the source for "${transform.src}" is an SVG. Pass it through unchanged by setting \`format="svg"\` on the component, or set \`image.dangerouslyProcessSVG: true\` to rasterize SVG sources.`
|
|
||||||
});
|
|
||||||
const result = sharp(inputBuffer, {
|
|
||||||
failOn: "none",
|
|
||||||
pages: -1,
|
|
||||||
limitInputPixels: config.service.config.limitInputPixels
|
|
||||||
});
|
|
||||||
result.rotate();
|
|
||||||
if (transform.width && transform.height) {
|
|
||||||
const fit = transform.fit ? fitMap[transform.fit] ?? "inside" : void 0;
|
|
||||||
result.resize({
|
|
||||||
width: Math.round(transform.width),
|
|
||||||
height: Math.round(transform.height),
|
|
||||||
kernel,
|
|
||||||
fit,
|
|
||||||
position: transform.position,
|
|
||||||
withoutEnlargement: true
|
|
||||||
});
|
|
||||||
} else if (transform.height && !transform.width) result.resize({
|
|
||||||
height: Math.round(transform.height),
|
|
||||||
withoutEnlargement: true,
|
|
||||||
kernel
|
|
||||||
});
|
|
||||||
else if (transform.width) result.resize({
|
|
||||||
width: Math.round(transform.width),
|
|
||||||
withoutEnlargement: true,
|
|
||||||
kernel
|
|
||||||
});
|
|
||||||
if (transform.background) result.flatten({ background: transform.background });
|
|
||||||
const encoderOptions = resolveSharpEncoderOptions({
|
|
||||||
format: outputFormat,
|
|
||||||
quality: transform.quality
|
|
||||||
}, bufferFormat, config.service.config);
|
|
||||||
if (outputFormat === "webp") result.webp(encoderOptions);
|
|
||||||
else if (outputFormat === "png") result.png(encoderOptions);
|
|
||||||
else if (outputFormat === "avif") result.avif(encoderOptions);
|
|
||||||
else if (outputFormat === "jpeg" || outputFormat === "jpg") result.jpeg(encoderOptions);
|
|
||||||
else result.toFormat(outputFormat, encoderOptions);
|
|
||||||
let data;
|
|
||||||
let info;
|
|
||||||
try {
|
|
||||||
({data, info} = await result.toBuffer({ resolveWithObject: true }));
|
|
||||||
} catch {
|
|
||||||
console.warn(`\u26A0\uFE0F Astro could not optimize image "${transform.src}". Sharp doesn't support this format. The image will be used unoptimized. Consider converting to WebP or placing in the public/ folder.`);
|
|
||||||
return {
|
|
||||||
data: inputBuffer,
|
|
||||||
format: bufferFormat
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
data: "buffer" in data && data.buffer instanceof SharedArrayBuffer ? new Uint8Array(data) : data,
|
|
||||||
format: info.format
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
export { sharp_default as default, resolveSharpEncoderOptions };
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
import { n as __exportAll, t as createComponent } from "./compiler_DiXpMM4i.mjs";
|
|
||||||
import { g as renderHead, m as renderTemplate, w as createAstro } from "./jsx-runtime_DseUv9uh.mjs";
|
|
||||||
//#region src/pages/social-card.astro
|
|
||||||
var social_card_exports = /* @__PURE__ */ __exportAll({
|
|
||||||
default: () => $$SocialCard,
|
|
||||||
file: () => $$file,
|
|
||||||
prerender: () => false,
|
|
||||||
url: () => $$url
|
|
||||||
});
|
|
||||||
createAstro("https://gabeefran.co");
|
|
||||||
var $$SocialCard = createComponent(($$result, $$props, $$slots) => {
|
|
||||||
const Astro = $$result.createAstro($$props, $$slots);
|
|
||||||
Astro.self = $$SocialCard;
|
|
||||||
const title = Astro.url.searchParams.get("title") || "Untitled";
|
|
||||||
const description = Astro.url.searchParams.get("description");
|
|
||||||
const author = Astro.url.searchParams.get("author");
|
|
||||||
return renderTemplate`<html data-astro-cid-3fpsgztt><head><meta charset="utf-8"><meta name="robots" content="noindex"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=JetBrains+Mono:wght@600&display=swap" rel="stylesheet">${renderHead($$result)}</head><body data-astro-cid-3fpsgztt><div class="brand" data-astro-cid-3fpsgztt><img src="/logo.png" alt="" data-astro-cid-3fpsgztt><span data-astro-cid-3fpsgztt>gabeefranco</span></div><h1 class="title" data-astro-cid-3fpsgztt>${title}</h1>${description && renderTemplate`<p class="description" data-astro-cid-3fpsgztt>${description}</p>`}${author && renderTemplate`<p class="author" data-astro-cid-3fpsgztt>${author}</p>`}</body></html>`;
|
|
||||||
}, "/home/gabe/src/gabeefran.co-blog/src/pages/social-card.astro", void 0);
|
|
||||||
var $$file = "/home/gabe/src/gabeefran.co-blog/src/pages/social-card.astro";
|
|
||||||
var $$url = "/social-card";
|
|
||||||
//#endregion
|
|
||||||
//#region \0virtual:astro:page:src/pages/social-card@_@astro
|
|
||||||
var page = () => social_card_exports;
|
|
||||||
//#endregion
|
|
||||||
export { page };
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
import { Fragment, createElement } from "react";
|
|
||||||
import { DOCUMENT_NODE, ELEMENT_NODE, TEXT_NODE, parse } from "ultrahtml";
|
|
||||||
//#region node_modules/@astrojs/react/dist/vnode-children.js
|
|
||||||
var ids = 0;
|
|
||||||
function convert(children) {
|
|
||||||
let doc = parse(children.toString().trim());
|
|
||||||
let id = ids++;
|
|
||||||
let key = 0;
|
|
||||||
function createReactElementFromNode(node) {
|
|
||||||
const childVnodes = Array.isArray(node.children) && node.children.length ? node.children.map((child) => createReactElementFromNode(child)).filter(Boolean) : void 0;
|
|
||||||
if (node.type === DOCUMENT_NODE) return createElement(Fragment, {}, childVnodes);
|
|
||||||
else if (node.type === ELEMENT_NODE) {
|
|
||||||
const { class: className, ...props } = node.attributes;
|
|
||||||
return createElement(node.name, {
|
|
||||||
...props,
|
|
||||||
className,
|
|
||||||
key: `${id}-${key++}`
|
|
||||||
}, childVnodes);
|
|
||||||
} else if (node.type === TEXT_NODE) return node.value.trim() ? node.value : void 0;
|
|
||||||
}
|
|
||||||
return createReactElementFromNode(doc).props.children;
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
export { convert as default };
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
//#region \0noop-middleware
|
|
||||||
var onRequest = (_, next) => next();
|
|
||||||
//#endregion
|
|
||||||
export { onRequest };
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
{
|
|
||||||
"version": 3,
|
|
||||||
"routes": [
|
|
||||||
{
|
|
||||||
"handle": "filesystem"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "^/_astro/(.*)$",
|
|
||||||
"headers": {
|
|
||||||
"cache-control": "public, max-age=31536000, immutable"
|
|
||||||
},
|
|
||||||
"continue": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "^/_server-islands/([^/]+?)/?$",
|
|
||||||
"dest": "_render"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "^/_image/?$",
|
|
||||||
"dest": "_render"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "^/page-card/?$",
|
|
||||||
"dest": "_render"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "^/social-card/?$",
|
|
||||||
"dest": "_render"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "^/.*$",
|
|
||||||
"dest": "/404.html",
|
|
||||||
"status": 404
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"runtime": "nodejs24.x",
|
|
||||||
"handler": ".vercel/output/server/entry.mjs",
|
|
||||||
"launcherType": "Nodejs",
|
|
||||||
"supportsResponseStreaming": true
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
//#region \0virtual:astro:server-island-manifest
|
|
||||||
var serverIslandMap = /* @__PURE__ */ new Map([]);
|
|
||||||
var serverIslandNameMap = /* @__PURE__ */ new Map([]);
|
|
||||||
//#endregion
|
|
||||||
export { serverIslandMap, serverIslandNameMap };
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
//#region \0virtual:astro:session-driver
|
|
||||||
var _virtual_astro_session_driver_default = null;
|
|
||||||
//#endregion
|
|
||||||
export { _virtual_astro_session_driver_default as default };
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
import { Z as AstroError, y as InvalidComponentArgs } from "./errors-data_zYnyWRO_.mjs";
|
|
||||||
import "./jsx-runtime_DseUv9uh.mjs";
|
|
||||||
//#region \0rolldown/runtime.js
|
|
||||||
var __defProp = Object.defineProperty;
|
|
||||||
var __exportAll = (all, no_symbols) => {
|
|
||||||
let target = {};
|
|
||||||
for (var name in all) __defProp(target, name, {
|
|
||||||
get: all[name],
|
|
||||||
enumerable: true
|
|
||||||
});
|
|
||||||
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
||||||
return target;
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/runtime/server/astro-component.js
|
|
||||||
function validateArgs(args) {
|
|
||||||
if (args.length !== 3) return false;
|
|
||||||
if (!args[0] || typeof args[0] !== "object") return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
function baseCreateComponent(cb, moduleId, propagation) {
|
|
||||||
const name = moduleId?.split("/").pop()?.replace(".astro", "") ?? "";
|
|
||||||
const fn = (...args) => {
|
|
||||||
if (!validateArgs(args)) throw new AstroError({
|
|
||||||
...InvalidComponentArgs,
|
|
||||||
message: InvalidComponentArgs.message(name)
|
|
||||||
});
|
|
||||||
return cb(...args);
|
|
||||||
};
|
|
||||||
Object.defineProperty(fn, "name", {
|
|
||||||
value: name,
|
|
||||||
writable: false
|
|
||||||
});
|
|
||||||
fn.isAstroComponentFactory = true;
|
|
||||||
fn.moduleId = moduleId;
|
|
||||||
fn.propagation = propagation;
|
|
||||||
return fn;
|
|
||||||
}
|
|
||||||
function createComponentWithOptions(opts) {
|
|
||||||
return baseCreateComponent(opts.factory, opts.moduleId, opts.propagation);
|
|
||||||
}
|
|
||||||
function createComponent(arg1, moduleId, propagation) {
|
|
||||||
if (typeof arg1 === "function") return baseCreateComponent(arg1, moduleId, propagation);
|
|
||||||
else return createComponentWithOptions(arg1);
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
export { __exportAll as n, createComponent as t };
|
|
||||||
|
|
@ -1,386 +0,0 @@
|
||||||
//#region node_modules/astro/dist/core/errors/utils.js
|
|
||||||
function normalizeLF(code) {
|
|
||||||
return code.replace(/\r\n|\r(?!\n)|\n/g, "\n");
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/core/errors/printer.js
|
|
||||||
function codeFrame(src, loc) {
|
|
||||||
if (!loc || loc.line === void 0 || loc.column === void 0) return "";
|
|
||||||
const lines = normalizeLF(src).split("\n").map((ln) => ln.replace(/\t/g, " "));
|
|
||||||
const visibleLines = [];
|
|
||||||
for (let n = -2; n <= 2; n++) if (lines[loc.line + n]) visibleLines.push(loc.line + n);
|
|
||||||
let gutterWidth = 0;
|
|
||||||
for (const lineNo of visibleLines) {
|
|
||||||
let w = `> ${lineNo}`;
|
|
||||||
if (w.length > gutterWidth) gutterWidth = w.length;
|
|
||||||
}
|
|
||||||
let output = "";
|
|
||||||
for (const lineNo of visibleLines) {
|
|
||||||
const isFocusedLine = lineNo === loc.line - 1;
|
|
||||||
output += isFocusedLine ? "> " : " ";
|
|
||||||
output += `${lineNo + 1} | ${lines[lineNo]}
|
|
||||||
`;
|
|
||||||
if (isFocusedLine) output += `${Array.from({ length: gutterWidth }).join(" ")} | ${Array.from({ length: loc.column }).join(" ")}^
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/core/errors/errors.js
|
|
||||||
function isAstroError(e) {
|
|
||||||
return e != null && (e instanceof AstroError || AstroError.is(e));
|
|
||||||
}
|
|
||||||
var AstroError = class extends Error {
|
|
||||||
loc;
|
|
||||||
title;
|
|
||||||
hint;
|
|
||||||
frame;
|
|
||||||
type = "AstroError";
|
|
||||||
constructor(props, options) {
|
|
||||||
const { name, title, message, stack, location, hint, frame } = props;
|
|
||||||
super(message, options);
|
|
||||||
this.title = title;
|
|
||||||
this.name = name;
|
|
||||||
if (message) this.message = message;
|
|
||||||
this.stack = stack ? stack : this.stack;
|
|
||||||
this.loc = location;
|
|
||||||
this.hint = hint;
|
|
||||||
this.frame = frame;
|
|
||||||
}
|
|
||||||
setLocation(location) {
|
|
||||||
this.loc = location;
|
|
||||||
}
|
|
||||||
setName(name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
setMessage(message) {
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
setHint(hint) {
|
|
||||||
this.hint = hint;
|
|
||||||
}
|
|
||||||
setFrame(source, location) {
|
|
||||||
this.frame = codeFrame(source, location);
|
|
||||||
}
|
|
||||||
static is(err) {
|
|
||||||
return err?.type === "AstroError";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var AstroUserError = class extends Error {
|
|
||||||
type = "AstroUserError";
|
|
||||||
/**
|
|
||||||
* A message that explains to the user how they can fix the error.
|
|
||||||
*/
|
|
||||||
hint;
|
|
||||||
name = "AstroUserError";
|
|
||||||
constructor(message, hint) {
|
|
||||||
super();
|
|
||||||
this.message = message;
|
|
||||||
this.hint = hint;
|
|
||||||
}
|
|
||||||
static is(err) {
|
|
||||||
return err?.type === "AstroUserError";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/core/errors/errors-data.js
|
|
||||||
var ClientAddressNotAvailable = {
|
|
||||||
name: "ClientAddressNotAvailable",
|
|
||||||
title: "`Astro.clientAddress` is not available in current adapter.",
|
|
||||||
message: (adapterName) => `\`Astro.clientAddress\` is not available in the \`${adapterName}\` adapter. File an issue with the adapter to add support.`
|
|
||||||
};
|
|
||||||
var PrerenderClientAddressNotAvailable = {
|
|
||||||
name: "PrerenderClientAddressNotAvailable",
|
|
||||||
title: "`Astro.clientAddress` cannot be used inside prerendered routes.",
|
|
||||||
message: (name) => `\`Astro.clientAddress\` cannot be used inside prerendered route ${name}.`
|
|
||||||
};
|
|
||||||
var StaticClientAddressNotAvailable = {
|
|
||||||
name: "StaticClientAddressNotAvailable",
|
|
||||||
title: "`Astro.clientAddress` is not available in prerendered pages.",
|
|
||||||
message: "`Astro.clientAddress` is only available on pages that are server-rendered.",
|
|
||||||
hint: "See https://docs.astro.build/en/guides/on-demand-rendering/ for more information on how to enable SSR."
|
|
||||||
};
|
|
||||||
var NoMatchingStaticPathFound = {
|
|
||||||
name: "NoMatchingStaticPathFound",
|
|
||||||
title: "No static path found for requested path.",
|
|
||||||
message: (pathName) => `A \`getStaticPaths()\` route pattern was matched, but no matching static path was found for requested path \`${pathName}\`.`,
|
|
||||||
hint: (possibleRoutes) => `Possible dynamic routes being matched: ${possibleRoutes.join(", ")}.`
|
|
||||||
};
|
|
||||||
var OnlyResponseCanBeReturned = {
|
|
||||||
name: "OnlyResponseCanBeReturned",
|
|
||||||
title: "Invalid type returned by Astro page.",
|
|
||||||
message: (route, returnedValue) => `Route \`${route ? route : ""}\` returned a \`${returnedValue}\`. Only a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) can be returned from Astro files.`,
|
|
||||||
hint: "See https://docs.astro.build/en/guides/on-demand-rendering/#response for more information."
|
|
||||||
};
|
|
||||||
var MissingMediaQueryDirective = {
|
|
||||||
name: "MissingMediaQueryDirective",
|
|
||||||
title: "Missing value for `client:media` directive.",
|
|
||||||
message: "Media query not provided for `client:media` directive. A media query similar to `client:media=\"(max-width: 600px)\"` must be provided."
|
|
||||||
};
|
|
||||||
var NoMatchingRenderer = {
|
|
||||||
name: "NoMatchingRenderer",
|
|
||||||
title: "No matching renderer found.",
|
|
||||||
message: (componentName, componentExtension, plural, validRenderersCount) => `Unable to render \`${componentName}\`.
|
|
||||||
|
|
||||||
${validRenderersCount > 0 ? `There ${plural ? "are" : "is"} ${validRenderersCount} renderer${plural ? "s" : ""} configured in your \`astro.config.mjs\` file,
|
|
||||||
but ${plural ? "none were" : "it was not"} able to server-side render \`${componentName}\`.` : `No valid renderer was found ${componentExtension ? `for the \`.${componentExtension}\` file extension.` : `for this file extension.`}`}`,
|
|
||||||
hint: (probableRenderers) => `Did you mean to enable the ${probableRenderers} integration?
|
|
||||||
|
|
||||||
See https://docs.astro.build/en/guides/framework-components/ for more information on how to install and configure integrations.`
|
|
||||||
};
|
|
||||||
var NoClientOnlyHint = {
|
|
||||||
name: "NoClientOnlyHint",
|
|
||||||
title: "Missing hint on client:only directive.",
|
|
||||||
message: (componentName) => `Unable to render \`${componentName}\`. When using the \`client:only\` hydration strategy, Astro needs a hint to use the correct renderer.`,
|
|
||||||
hint: (probableRenderers) => `Did you mean to pass \`client:only="${probableRenderers}"\`? See https://docs.astro.build/en/reference/directives-reference/#clientonly for more information on \`client:only\`.`
|
|
||||||
};
|
|
||||||
var InvalidGetStaticPathsEntry = {
|
|
||||||
name: "InvalidGetStaticPathsEntry",
|
|
||||||
title: "Invalid entry inside `getStaticPaths()`'s return value.",
|
|
||||||
message: (entryType) => `Invalid entry returned by \`getStaticPaths()\`. Expected an object, got \`${entryType}\`.`,
|
|
||||||
hint: "If you're using a `.map` call, you might be looking for `.flatMap()` instead. See https://docs.astro.build/en/reference/routing-reference/#getstaticpaths for more information on `getStaticPaths()`."
|
|
||||||
};
|
|
||||||
var InvalidGetStaticPathsReturn = {
|
|
||||||
name: "InvalidGetStaticPathsReturn",
|
|
||||||
title: "Invalid value returned by `getStaticPaths()`.",
|
|
||||||
message: (returnType) => `Invalid type returned by \`getStaticPaths()\`. Expected an \`array\`, got \`${returnType}\`.`,
|
|
||||||
hint: "See https://docs.astro.build/en/reference/routing-reference/#getstaticpaths for more information on `getStaticPaths()`."
|
|
||||||
};
|
|
||||||
var GetStaticPathsExpectedParams = {
|
|
||||||
name: "GetStaticPathsExpectedParams",
|
|
||||||
title: "Missing params property on `getStaticPaths()` route.",
|
|
||||||
message: "Missing or empty required `params` property on `getStaticPaths()` route.",
|
|
||||||
hint: "See https://docs.astro.build/en/reference/routing-reference/#getstaticpaths for more information on `getStaticPaths()`."
|
|
||||||
};
|
|
||||||
var GetStaticPathsInvalidRouteParam = {
|
|
||||||
name: "GetStaticPathsInvalidRouteParam",
|
|
||||||
title: "Invalid route parameter returned by `getStaticPaths()`.",
|
|
||||||
message: (key, value, valueType) => `Invalid \`getStaticPaths()\` route parameter for \`${key}\`. Expected a string or undefined, received \`${valueType}\` (\`${value}\`).`,
|
|
||||||
hint: "See https://docs.astro.build/en/reference/routing-reference/#getstaticpaths for more information on `getStaticPaths()`."
|
|
||||||
};
|
|
||||||
var GetStaticPathsRequired = {
|
|
||||||
name: "GetStaticPathsRequired",
|
|
||||||
title: "`getStaticPaths()` function required for dynamic routes.",
|
|
||||||
message: "`getStaticPaths()` function is required for dynamic routes. Make sure that you `export` a `getStaticPaths()` function from your dynamic route.",
|
|
||||||
hint: `See https://docs.astro.build/en/guides/routing/#dynamic-routes for more information on dynamic routes.
|
|
||||||
|
|
||||||
If you meant for this route to be server-rendered, set \`export const prerender = false;\` in the page.`
|
|
||||||
};
|
|
||||||
var ReservedSlotName = {
|
|
||||||
name: "ReservedSlotName",
|
|
||||||
title: "Invalid slot name.",
|
|
||||||
message: (slotName) => `Unable to create a slot named \`${slotName}\`. \`${slotName}\` is a reserved slot name. Please update the name of this slot.`
|
|
||||||
};
|
|
||||||
var NoMatchingImport = {
|
|
||||||
name: "NoMatchingImport",
|
|
||||||
title: "No import found for component.",
|
|
||||||
message: (componentName) => `Could not render \`${componentName}\`. No matching import has been found for \`${componentName}\`.`,
|
|
||||||
hint: "Please make sure the component is properly imported."
|
|
||||||
};
|
|
||||||
var InvalidComponentArgs = {
|
|
||||||
name: "InvalidComponentArgs",
|
|
||||||
title: "Invalid component arguments.",
|
|
||||||
message: (name) => `Invalid arguments passed to${name ? ` <${name}>` : ""} component.`,
|
|
||||||
hint: "Astro components cannot be rendered directly via function call, such as `Component()` or `{items.map(Component)}`."
|
|
||||||
};
|
|
||||||
var PageNumberParamNotFound = {
|
|
||||||
name: "PageNumberParamNotFound",
|
|
||||||
title: "Page number param not found.",
|
|
||||||
message: (paramName) => `[paginate()] page number param \`${paramName}\` not found in your filepath.`,
|
|
||||||
hint: "Rename your file to `[page].astro` or `[...page].astro`."
|
|
||||||
};
|
|
||||||
var ImageMissingAlt = {
|
|
||||||
name: "ImageMissingAlt",
|
|
||||||
title: "Image missing required \"alt\" property.",
|
|
||||||
message: "Image missing \"alt\" property. \"alt\" text is required to describe important images on the page.",
|
|
||||||
hint: "Use an empty string (\"\") for decorative images."
|
|
||||||
};
|
|
||||||
var InvalidImageService = {
|
|
||||||
name: "InvalidImageService",
|
|
||||||
title: "Error while loading image service.",
|
|
||||||
message: "There was an error loading the configured image service. Please see the stack trace for more information."
|
|
||||||
};
|
|
||||||
var MissingImageDimension = {
|
|
||||||
name: "MissingImageDimension",
|
|
||||||
title: "Missing image dimensions.",
|
|
||||||
message: (missingDimension, imageURL) => `Missing ${missingDimension === "both" ? "width and height attributes" : `${missingDimension} attribute`} for ${imageURL}. When using remote images, both dimensions are required in order to avoid CLS.`,
|
|
||||||
hint: "If your image is inside your `src` folder, you probably meant to import it instead. See [the Imports guide for more information](https://docs.astro.build/en/guides/imports/#other-assets). You can also use `inferSize={true}` for remote images to get the original dimensions."
|
|
||||||
};
|
|
||||||
var FailedToFetchRemoteImageDimensions = {
|
|
||||||
name: "FailedToFetchRemoteImageDimensions",
|
|
||||||
title: "Failed to retrieve remote image dimensions.",
|
|
||||||
message: (imageURL) => `Failed to get the dimensions for ${imageURL}.`,
|
|
||||||
hint: "Verify your remote image URL is accurate, and that you are not using `inferSize` with a file located in your `public/` folder."
|
|
||||||
};
|
|
||||||
var RemoteImageNotAllowed = {
|
|
||||||
name: "RemoteImageNotAllowed",
|
|
||||||
title: "Remote image is not allowed.",
|
|
||||||
message: (imageURL) => `Remote image ${imageURL} is not allowed by your image configuration.`,
|
|
||||||
hint: "Update `image.domains` or `image.remotePatterns`, or remove `inferSize` for this image."
|
|
||||||
};
|
|
||||||
var UnsupportedImageFormat = {
|
|
||||||
name: "UnsupportedImageFormat",
|
|
||||||
title: "Unsupported image format.",
|
|
||||||
message: (format, imagePath, supportedFormats) => `Received unsupported format \`${format}\` from \`${imagePath}\`. Currently only ${supportedFormats.join(", ")} are supported by our image services.`,
|
|
||||||
hint: "Using an `img` tag directly instead of the `Image` component might be what you're looking for."
|
|
||||||
};
|
|
||||||
var UnsupportedImageConversion = {
|
|
||||||
name: "UnsupportedImageConversion",
|
|
||||||
title: "Unsupported image conversion.",
|
|
||||||
message: "Converting between vector (such as SVGs) and raster (such as PNGs and JPEGs) images is not currently supported."
|
|
||||||
};
|
|
||||||
var PrerenderDynamicEndpointPathCollide = {
|
|
||||||
name: "PrerenderDynamicEndpointPathCollide",
|
|
||||||
title: "Prerendered dynamic endpoint has path collision.",
|
|
||||||
message: (pathname) => `Could not render \`${pathname}\` with an \`undefined\` param as the generated path will collide during prerendering. Prevent passing \`undefined\` as \`params\` for the endpoint's \`getStaticPaths()\` function, or add an additional extension to the endpoint's filename.`,
|
|
||||||
hint: (filename) => `Rename \`${filename}\` to \`${filename.replace(/\.(?:js|ts)/, (m) => `.json` + m)}\``
|
|
||||||
};
|
|
||||||
var ExpectedImage = {
|
|
||||||
name: "ExpectedImage",
|
|
||||||
title: "Expected src to be an image.",
|
|
||||||
message: (src, typeofOptions, fullOptions) => `Expected \`src\` property for \`getImage\` or \`<Image />\` to be either an ESM imported image or a string with the path of a remote image. Received \`${src}\` (type: \`${typeofOptions}\`).
|
|
||||||
|
|
||||||
Full serialized options received: \`${fullOptions}\`.`,
|
|
||||||
hint: "This error can often happen because of a wrong path. Make sure the path to your image is correct. If you're passing an async function, make sure to call and await it."
|
|
||||||
};
|
|
||||||
var ExpectedImageOptions = {
|
|
||||||
name: "ExpectedImageOptions",
|
|
||||||
title: "Expected image options.",
|
|
||||||
message: (options) => `Expected \`getImage()\` parameter to be an object. Received \`${options}\`.`
|
|
||||||
};
|
|
||||||
var ExpectedNotESMImage = {
|
|
||||||
name: "ExpectedNotESMImage",
|
|
||||||
title: "Expected image options, not an ESM-imported image.",
|
|
||||||
message: "An ESM-imported image cannot be passed directly to `getImage()`. Instead, pass an object with the image in the `src` property.",
|
|
||||||
hint: "Try changing `getImage(myImage)` to `getImage({ src: myImage })`"
|
|
||||||
};
|
|
||||||
var IncompatibleDescriptorOptions = {
|
|
||||||
name: "IncompatibleDescriptorOptions",
|
|
||||||
title: "Cannot set both `densities` and `widths`.",
|
|
||||||
message: "Only one of `densities` or `widths` can be specified. In most cases, you'll probably want to use only `widths` if you require specific widths.",
|
|
||||||
hint: "Those attributes are used to construct a `srcset` attribute, which cannot have both `x` and `w` descriptors."
|
|
||||||
};
|
|
||||||
var NoImageMetadata = {
|
|
||||||
name: "NoImageMetadata",
|
|
||||||
title: "Could not process image metadata.",
|
|
||||||
message: (imagePath) => `Could not process image metadata${imagePath ? ` for \`${imagePath}\`` : ""}.`,
|
|
||||||
hint: "This is often caused by a corrupted or malformed image. Re-exporting the image from your image editor may fix this issue."
|
|
||||||
};
|
|
||||||
var ResponseSentError = {
|
|
||||||
name: "ResponseSentError",
|
|
||||||
title: "Unable to set response.",
|
|
||||||
message: "The response has already been sent to the browser and cannot be altered."
|
|
||||||
};
|
|
||||||
var MiddlewareNoDataOrNextCalled = {
|
|
||||||
name: "MiddlewareNoDataOrNextCalled",
|
|
||||||
title: "The middleware didn't return a `Response`.",
|
|
||||||
message: "Make sure your middleware returns a `Response` object, either directly or by returning the `Response` from calling the `next` function."
|
|
||||||
};
|
|
||||||
var MiddlewareNotAResponse = {
|
|
||||||
name: "MiddlewareNotAResponse",
|
|
||||||
title: "The middleware returned something that is not a `Response` object.",
|
|
||||||
message: "Any data returned from middleware must be a valid `Response` object."
|
|
||||||
};
|
|
||||||
var EndpointDidNotReturnAResponse = {
|
|
||||||
name: "EndpointDidNotReturnAResponse",
|
|
||||||
title: "The endpoint did not return a `Response`.",
|
|
||||||
message: "An endpoint must return either a `Response`, or a `Promise` that resolves with a `Response`."
|
|
||||||
};
|
|
||||||
var LocalsNotAnObject = {
|
|
||||||
name: "LocalsNotAnObject",
|
|
||||||
title: "Value assigned to `locals` is not accepted.",
|
|
||||||
message: "`locals` can only be assigned to an object. Other values like numbers, strings, etc. are not accepted.",
|
|
||||||
hint: "If you tried to remove some information from the `locals` object, try to use `delete` or set the property to `undefined`."
|
|
||||||
};
|
|
||||||
var LocalsReassigned = {
|
|
||||||
name: "LocalsReassigned",
|
|
||||||
title: "`locals` must not be reassigned.",
|
|
||||||
message: "`locals` cannot be assigned directly.",
|
|
||||||
hint: "Set a `locals` property instead."
|
|
||||||
};
|
|
||||||
var AstroResponseHeadersReassigned = {
|
|
||||||
name: "AstroResponseHeadersReassigned",
|
|
||||||
title: "`Astro.response.headers` must not be reassigned.",
|
|
||||||
message: "Individual headers can be added to and removed from `Astro.response.headers`, but it must not be replaced with another instance of `Headers` altogether.",
|
|
||||||
hint: "Consider using `Astro.response.headers.add()`, and `Astro.response.headers.delete()`."
|
|
||||||
};
|
|
||||||
var LocalImageUsedWrongly = {
|
|
||||||
name: "LocalImageUsedWrongly",
|
|
||||||
title: "Local images must be imported.",
|
|
||||||
message: (imageFilePath) => `\`Image\`'s and \`getImage\`'s \`src\` parameter must be an imported image or a URL, it cannot be a string filepath. Received \`${imageFilePath}\`.`,
|
|
||||||
hint: "If you want to use an image from your `src` folder, you need to either import it or if the image is coming from a content collection, use the [image() schema helper](https://docs.astro.build/en/guides/images/#images-in-content-collections). See https://docs.astro.build/en/reference/modules/astro-assets/#src-required for more information on the `src` property."
|
|
||||||
};
|
|
||||||
var MissingSharp = {
|
|
||||||
name: "MissingSharp",
|
|
||||||
title: "Could not find Sharp.",
|
|
||||||
message: "Could not find Sharp. Please install Sharp (`sharp`) manually into your project or migrate to another image service.",
|
|
||||||
hint: "See Sharp's installation instructions for more information: https://sharp.pixelplumbing.com/install. If you are not relying on `astro:assets` to optimize, transform, or process any images, you can configure a passthrough image service instead of installing Sharp. See https://docs.astro.build/en/reference/errors/missing-sharp for more information.\n\nSee https://docs.astro.build/en/guides/images/#default-image-service for more information on how to migrate to another image service."
|
|
||||||
};
|
|
||||||
var i18nNoLocaleFoundInPath = {
|
|
||||||
name: "i18nNoLocaleFoundInPath",
|
|
||||||
title: "The path doesn't contain any locale.",
|
|
||||||
message: "You tried to use an i18n utility on a path that doesn't contain any locale. You can use `pathHasLocale` first to determine if the path has a locale."
|
|
||||||
};
|
|
||||||
var RewriteWithBodyUsed = {
|
|
||||||
name: "RewriteWithBodyUsed",
|
|
||||||
title: "Cannot use `Astro.rewrite()` after the request body has been read.",
|
|
||||||
message: "`Astro.rewrite()` cannot be used if the request body has already been read. If you need to read the body, first clone the request."
|
|
||||||
};
|
|
||||||
var ForbiddenRewrite = {
|
|
||||||
name: "ForbiddenRewrite",
|
|
||||||
title: "Forbidden rewrite to a static route.",
|
|
||||||
message: (from, to, component) => `You tried to rewrite the on-demand route '${from}' with the static route '${to}', when using the 'server' output.
|
|
||||||
|
|
||||||
The static route '${to}' is rendered by the component
|
|
||||||
'${component}', which is marked as prerendered. This is a forbidden operation because during the build, the component '${component}' is compiled to an
|
|
||||||
HTML file, which can't be retrieved at runtime by Astro.`,
|
|
||||||
hint: (component) => `Add \`export const prerender = false\` to the component '${component}', or use \`Astro.redirect()\`.`
|
|
||||||
};
|
|
||||||
var FontFamilyNotFound = {
|
|
||||||
name: "FontFamilyNotFound",
|
|
||||||
title: "Font family not found.",
|
|
||||||
message: (family) => `No data was found for the \`"${family}"\` family passed to the \`<Font>\` component.`,
|
|
||||||
hint: "This is often caused by a typo. Check that the `<Font />` component is using a `cssVariable` specified in your config."
|
|
||||||
};
|
|
||||||
var FontFileUrlNotFound = {
|
|
||||||
name: "FontFileUrlNotFound",
|
|
||||||
title: "Font file URL not found.",
|
|
||||||
message: (url) => `The \`"${url}"\` URL passed to the \`experimental_getFontFileURL()\` function is invalid.`,
|
|
||||||
hint: "Make sure you pass a valid URL, obtained via the `fontData` object."
|
|
||||||
};
|
|
||||||
var MissingGetFontFileRequestUrl = {
|
|
||||||
name: "MissingGetFontFileRequestUrl",
|
|
||||||
title: "`experimental_getFontFileURL()` requires the request URL with on-demand rendering.",
|
|
||||||
hint: "Pass the request URL as the 2nd argument, for example `Astro.url`."
|
|
||||||
};
|
|
||||||
var UnavailableAstroGlobal = {
|
|
||||||
name: "UnavailableAstroGlobal",
|
|
||||||
title: "Unavailable Astro global in `getStaticPaths()`.",
|
|
||||||
message: (name) => `The Astro global is not available in this scope. Please remove \`Astro.${name}\` from your \`getStaticPaths()\` function.`
|
|
||||||
};
|
|
||||||
var NoManifestAvailable = {
|
|
||||||
name: "NoManifestAvailableError",
|
|
||||||
title: "No manifest available.",
|
|
||||||
message: "`new FetchState(request)` was called outside of an Astro server, so no manifest is available.",
|
|
||||||
hint: "Make sure this code runs as part of your Astro app, such as its fetch entrypoint. If this error occurred inside an Astro-built server, please open an issue at https://github.com/withastro/astro/issues."
|
|
||||||
};
|
|
||||||
var ActionsReturnedInvalidDataError = {
|
|
||||||
name: "ActionsReturnedInvalidDataError",
|
|
||||||
title: "Action handler returned invalid data.",
|
|
||||||
message: (error) => `Action handler returned invalid data. Handlers should return serializable data types like objects, arrays, strings, and numbers. Parse error: ${error}`,
|
|
||||||
hint: "See the devalue library for all supported types: https://github.com/rich-harris/devalue"
|
|
||||||
};
|
|
||||||
var ActionNotFoundError = {
|
|
||||||
name: "ActionNotFoundError",
|
|
||||||
title: "Action not found.",
|
|
||||||
message: (actionName) => `The server received a request for an action named \`${actionName}\` but could not find a match. If you renamed an action, check that you've updated your \`actions/index\` file and your calling code to match.`,
|
|
||||||
hint: "You can run `astro check` to detect type errors caused by mismatched action names."
|
|
||||||
};
|
|
||||||
var CacheNotEnabled = {
|
|
||||||
name: "CacheNotEnabled",
|
|
||||||
title: "Cache is not enabled.",
|
|
||||||
message: "`Astro.cache` is not available because the cache feature is not enabled. To use caching, configure a cache provider in your Astro config under `cache`.",
|
|
||||||
hint: "Use an adapter that provides a default cache provider, or set one explicitly: `cache: { provider: \"...\" }`. See https://docs.astro.build/en/guides/caching/."
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
export { isAstroError as $, MissingMediaQueryDirective as A, PrerenderClientAddressNotAvailable as B, LocalImageUsedWrongly as C, MiddlewareNotAResponse as D, MiddlewareNoDataOrNextCalled as E, NoMatchingImport as F, RewriteWithBodyUsed as G, RemoteImageNotAllowed as H, NoMatchingRenderer as I, UnsupportedImageConversion as J, StaticClientAddressNotAvailable as K, NoMatchingStaticPathFound as L, NoClientOnlyHint as M, NoImageMetadata as N, MissingGetFontFileRequestUrl as O, NoManifestAvailable as P, AstroUserError as Q, OnlyResponseCanBeReturned as R, InvalidImageService as S, LocalsReassigned as T, ReservedSlotName as U, PrerenderDynamicEndpointPathCollide as V, ResponseSentError as W, i18nNoLocaleFoundInPath as X, UnsupportedImageFormat as Y, AstroError as Z, ImageMissingAlt as _, ClientAddressNotAvailable as a, InvalidGetStaticPathsEntry as b, ExpectedImageOptions as c, FontFamilyNotFound as d, FontFileUrlNotFound as f, GetStaticPathsRequired as g, GetStaticPathsInvalidRouteParam as h, CacheNotEnabled as i, MissingSharp as j, MissingImageDimension as k, ExpectedNotESMImage as l, GetStaticPathsExpectedParams as m, ActionsReturnedInvalidDataError as n, EndpointDidNotReturnAResponse as o, ForbiddenRewrite as p, UnavailableAstroGlobal as q, AstroResponseHeadersReassigned as r, ExpectedImage as s, ActionNotFoundError as t, FailedToFetchRemoteImageDimensions as u, IncompatibleDescriptorOptions as v, LocalsNotAnObject as w, InvalidGetStaticPathsReturn as x, InvalidComponentArgs as y, PageNumberParamNotFound as z };
|
|
||||||
|
|
@ -1,720 +0,0 @@
|
||||||
import { n as __exportAll, t as createComponent } from "./compiler_DiXpMM4i.mjs";
|
|
||||||
import { C as LocalImageUsedWrongly, H as RemoteImageNotAllowed, J as UnsupportedImageConversion, O as MissingGetFontFileRequestUrl, S as InvalidImageService, Y as UnsupportedImageFormat, Z as AstroError, _ as ImageMissingAlt, c as ExpectedImageOptions, d as FontFamilyNotFound, k as MissingImageDimension, l as ExpectedNotESMImage, s as ExpectedImage, v as IncompatibleDescriptorOptions } from "./errors-data_zYnyWRO_.mjs";
|
|
||||||
import { _ as inferRemoteSize$1, b as isRemoteAllowed, c as isRemotePath, f as removeQueryString, l as joinPaths, v as fetchWithRedirects } from "./path_BQC-SKOp.mjs";
|
|
||||||
import { S as unescapeHTML, _ as addAttribute, h as maybeRenderHead, m as renderTemplate, r as spreadAttributes, w as createAstro } from "./jsx-runtime_DseUv9uh.mjs";
|
|
||||||
import * as mime from "mrmime";
|
|
||||||
//#region node_modules/astro/dist/assets/utils/imageKind.js
|
|
||||||
function isESMImportedImage(src) {
|
|
||||||
return typeof src === "object" || typeof src === "function" && "src" in src;
|
|
||||||
}
|
|
||||||
function isRemoteImage(src) {
|
|
||||||
return typeof src === "string";
|
|
||||||
}
|
|
||||||
async function resolveSrc(src) {
|
|
||||||
if (typeof src === "object" && "then" in src) {
|
|
||||||
const resource = await src;
|
|
||||||
return resource.default ?? resource;
|
|
||||||
}
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
var VALID_SUPPORTED_FORMATS = [
|
|
||||||
"jpeg",
|
|
||||||
"jpg",
|
|
||||||
"png",
|
|
||||||
"tiff",
|
|
||||||
"webp",
|
|
||||||
"gif",
|
|
||||||
"svg",
|
|
||||||
"avif"
|
|
||||||
];
|
|
||||||
var DEFAULT_OUTPUT_FORMAT = "webp";
|
|
||||||
var DEFAULT_HASH_PROPS = [
|
|
||||||
"src",
|
|
||||||
"width",
|
|
||||||
"height",
|
|
||||||
"format",
|
|
||||||
"quality",
|
|
||||||
"fit",
|
|
||||||
"position",
|
|
||||||
"background"
|
|
||||||
];
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/layout.js
|
|
||||||
var DEFAULT_RESOLUTIONS = [
|
|
||||||
640,
|
|
||||||
750,
|
|
||||||
828,
|
|
||||||
960,
|
|
||||||
1080,
|
|
||||||
1280,
|
|
||||||
1668,
|
|
||||||
1920,
|
|
||||||
2048,
|
|
||||||
2560,
|
|
||||||
3200,
|
|
||||||
3840,
|
|
||||||
4480,
|
|
||||||
5120,
|
|
||||||
6016
|
|
||||||
];
|
|
||||||
var LIMITED_RESOLUTIONS = [
|
|
||||||
640,
|
|
||||||
750,
|
|
||||||
828,
|
|
||||||
1080,
|
|
||||||
1280,
|
|
||||||
1668,
|
|
||||||
2048,
|
|
||||||
2560
|
|
||||||
];
|
|
||||||
var getWidths = ({ width, layout, breakpoints = DEFAULT_RESOLUTIONS, originalWidth }) => {
|
|
||||||
const smallerThanOriginal = (w) => !originalWidth || w <= originalWidth;
|
|
||||||
if (layout === "full-width") return breakpoints.filter(smallerThanOriginal);
|
|
||||||
if (!width) return [];
|
|
||||||
const doubleWidth = width * 2;
|
|
||||||
const maxSize = originalWidth ? Math.min(doubleWidth, originalWidth) : doubleWidth;
|
|
||||||
if (layout === "fixed") return originalWidth && width > originalWidth ? [originalWidth] : [width, maxSize];
|
|
||||||
if (layout === "constrained") return [
|
|
||||||
width,
|
|
||||||
doubleWidth,
|
|
||||||
...breakpoints
|
|
||||||
].filter((w) => w <= maxSize).sort((a, b) => a - b);
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
var getSizesAttribute = ({ width, layout }) => {
|
|
||||||
if (!width || !layout) return;
|
|
||||||
switch (layout) {
|
|
||||||
case "constrained": return `(min-width: ${width}px) ${width}px, 100vw`;
|
|
||||||
case "fixed": return `${width}px`;
|
|
||||||
case "full-width": return `100vw`;
|
|
||||||
default: return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/utils/inferSourceFormat.js
|
|
||||||
var DATA_PREFIX = "data:";
|
|
||||||
function inferSourceFormat(src) {
|
|
||||||
if (src.startsWith(DATA_PREFIX)) {
|
|
||||||
const sepIndex = src.indexOf(";");
|
|
||||||
const commaIndex = src.indexOf(",");
|
|
||||||
const mimeEnd = sepIndex === -1 ? commaIndex : commaIndex === -1 ? sepIndex : Math.min(sepIndex, commaIndex);
|
|
||||||
if (mimeEnd === -1) return void 0;
|
|
||||||
const mime = src.slice(5, mimeEnd);
|
|
||||||
if (mime === "image/svg+xml") return "svg";
|
|
||||||
return mime.split("/")[1] || void 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const cleanSrc = removeQueryString(src).split("#")[0];
|
|
||||||
const lastSlash = cleanSrc.lastIndexOf("/");
|
|
||||||
const basename = lastSlash === -1 ? cleanSrc : cleanSrc.slice(lastSlash + 1);
|
|
||||||
const lastDot = basename.lastIndexOf(".");
|
|
||||||
if (lastDot === -1) return void 0;
|
|
||||||
return basename.slice(lastDot + 1).toLowerCase();
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function resolveDefaultOutputFormat(sourceFormat) {
|
|
||||||
return sourceFormat === "svg" ? "svg" : DEFAULT_OUTPUT_FORMAT;
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/services/service.js
|
|
||||||
function isLocalService(service) {
|
|
||||||
if (!service) return false;
|
|
||||||
return "transform" in service;
|
|
||||||
}
|
|
||||||
function parseQuality(quality) {
|
|
||||||
let result = Number.parseInt(quality);
|
|
||||||
if (Number.isNaN(result)) return quality;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
var sortNumeric = (a, b) => a - b;
|
|
||||||
function verifyOptions(options) {
|
|
||||||
if (!options.src || !isRemoteImage(options.src) && !isESMImportedImage(options.src)) throw new AstroError({
|
|
||||||
...ExpectedImage,
|
|
||||||
message: ExpectedImage.message(JSON.stringify(options.src), typeof options.src, JSON.stringify(options, (_, v) => v === void 0 ? null : v))
|
|
||||||
});
|
|
||||||
if (!isESMImportedImage(options.src)) {
|
|
||||||
if (options.src.startsWith("/@fs/") || !isRemotePath(options.src) && !options.src.startsWith("/")) throw new AstroError({
|
|
||||||
...LocalImageUsedWrongly,
|
|
||||||
message: LocalImageUsedWrongly.message(options.src)
|
|
||||||
});
|
|
||||||
let missingDimension;
|
|
||||||
if (!options.width && !options.height) missingDimension = "both";
|
|
||||||
else if (!options.width && options.height) missingDimension = "width";
|
|
||||||
else if (options.width && !options.height) missingDimension = "height";
|
|
||||||
if (missingDimension) throw new AstroError({
|
|
||||||
...MissingImageDimension,
|
|
||||||
message: MissingImageDimension.message(missingDimension, options.src)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (!VALID_SUPPORTED_FORMATS.includes(options.src.format)) throw new AstroError({
|
|
||||||
...UnsupportedImageFormat,
|
|
||||||
message: UnsupportedImageFormat.message(options.src.format, options.src.src, VALID_SUPPORTED_FORMATS)
|
|
||||||
});
|
|
||||||
if (options.widths && options.densities) throw new AstroError(IncompatibleDescriptorOptions);
|
|
||||||
if (options.src.format !== "svg" && options.format === "svg") throw new AstroError(UnsupportedImageConversion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var baseService = {
|
|
||||||
propertiesToHash: DEFAULT_HASH_PROPS,
|
|
||||||
validateOptions(options) {
|
|
||||||
verifyOptions(options);
|
|
||||||
if (!options.format) {
|
|
||||||
if (isESMImportedImage(options.src)) options.format = resolveDefaultOutputFormat(options.src.format);
|
|
||||||
else {
|
|
||||||
const inferred = inferSourceFormat(options.src);
|
|
||||||
if (inferred) options.format = resolveDefaultOutputFormat(inferred);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (options.width) options.width = Math.round(options.width);
|
|
||||||
if (options.height) options.height = Math.round(options.height);
|
|
||||||
if (options.layout) delete options.layout;
|
|
||||||
if (options.fit === "none") delete options.fit;
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
getHTMLAttributes(options) {
|
|
||||||
const { targetWidth, targetHeight } = getTargetDimensions(options);
|
|
||||||
const { src, width, height, format, quality, densities, widths, formats, layout, priority, fit, position, background, ...attributes } = options;
|
|
||||||
return {
|
|
||||||
...attributes,
|
|
||||||
width: targetWidth,
|
|
||||||
height: targetHeight,
|
|
||||||
loading: attributes.loading ?? "lazy",
|
|
||||||
decoding: attributes.decoding ?? "async"
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getSrcSet(options) {
|
|
||||||
const { targetWidth, targetHeight } = getTargetDimensions(options);
|
|
||||||
const aspectRatio = targetWidth / targetHeight;
|
|
||||||
const { widths, densities } = options;
|
|
||||||
const targetFormat = options.format;
|
|
||||||
let transformedWidths = (widths ?? []).sort(sortNumeric);
|
|
||||||
let imageWidth = options.width;
|
|
||||||
let maxWidth = Number.POSITIVE_INFINITY;
|
|
||||||
if (isESMImportedImage(options.src)) {
|
|
||||||
imageWidth = options.src.width;
|
|
||||||
maxWidth = imageWidth;
|
|
||||||
if (transformedWidths.length > 0 && transformedWidths.at(-1) > maxWidth) {
|
|
||||||
transformedWidths = transformedWidths.filter((width) => width <= maxWidth);
|
|
||||||
transformedWidths.push(maxWidth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
transformedWidths = Array.from(new Set(transformedWidths));
|
|
||||||
const { width: transformWidth, height: transformHeight, ...transformWithoutDimensions } = options;
|
|
||||||
let allWidths = [];
|
|
||||||
if (densities) {
|
|
||||||
const densityValues = densities.map((density) => {
|
|
||||||
if (typeof density === "number") return density;
|
|
||||||
else return Number.parseFloat(density);
|
|
||||||
});
|
|
||||||
allWidths = densityValues.sort(sortNumeric).map((density) => Math.round(targetWidth * density)).map((width, index) => ({
|
|
||||||
width,
|
|
||||||
descriptor: `${densityValues[index]}x`
|
|
||||||
}));
|
|
||||||
} else if (transformedWidths.length > 0) allWidths = transformedWidths.map((width) => ({
|
|
||||||
width,
|
|
||||||
descriptor: `${width}w`
|
|
||||||
}));
|
|
||||||
return allWidths.map(({ width, descriptor }) => {
|
|
||||||
const height = Math.round(width / aspectRatio);
|
|
||||||
return {
|
|
||||||
transform: {
|
|
||||||
...transformWithoutDimensions,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
},
|
|
||||||
descriptor,
|
|
||||||
attributes: targetFormat ? { type: `image/${targetFormat}` } : {}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getURL(options, imageConfig) {
|
|
||||||
const searchParams = new URLSearchParams();
|
|
||||||
if (isESMImportedImage(options.src)) searchParams.append("href", options.src.src);
|
|
||||||
else if (isRemoteAllowed(options.src, imageConfig)) searchParams.append("href", options.src);
|
|
||||||
else return options.src;
|
|
||||||
Object.entries({
|
|
||||||
w: "width",
|
|
||||||
h: "height",
|
|
||||||
q: "quality",
|
|
||||||
f: "format",
|
|
||||||
fit: "fit",
|
|
||||||
position: "position",
|
|
||||||
background: "background"
|
|
||||||
}).forEach(([param, key]) => {
|
|
||||||
options[key] && searchParams.append(param, options[key].toString());
|
|
||||||
});
|
|
||||||
let url = `${joinPaths("/", imageConfig.endpoint.route)}?${searchParams}`;
|
|
||||||
if (imageConfig.assetQueryParams) {
|
|
||||||
const assetQueryString = imageConfig.assetQueryParams.toString();
|
|
||||||
if (assetQueryString) url += "&" + assetQueryString;
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
},
|
|
||||||
parseURL(url) {
|
|
||||||
const params = url.searchParams;
|
|
||||||
if (!params.has("href")) return;
|
|
||||||
return {
|
|
||||||
src: params.get("href"),
|
|
||||||
width: params.has("w") ? Number.parseInt(params.get("w")) : void 0,
|
|
||||||
height: params.has("h") ? Number.parseInt(params.get("h")) : void 0,
|
|
||||||
format: params.has("f") ? params.get("f") : void 0,
|
|
||||||
quality: params.get("q"),
|
|
||||||
fit: params.get("fit"),
|
|
||||||
position: params.get("position") ?? void 0,
|
|
||||||
background: params.get("background") ?? void 0
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getRemoteSize(url, imageConfig) {
|
|
||||||
return inferRemoteSize$1(url, imageConfig);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
function getTargetDimensions(options) {
|
|
||||||
let targetWidth = options.width;
|
|
||||||
let targetHeight = options.height;
|
|
||||||
if (isESMImportedImage(options.src)) {
|
|
||||||
const aspectRatio = options.src.width / options.src.height;
|
|
||||||
if (targetHeight && !targetWidth) targetWidth = Math.round(targetHeight * aspectRatio);
|
|
||||||
else if (targetWidth && !targetHeight) targetHeight = Math.round(targetWidth / aspectRatio);
|
|
||||||
else if (!targetWidth && !targetHeight) {
|
|
||||||
targetWidth = options.src.width;
|
|
||||||
targetHeight = options.src.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
targetWidth,
|
|
||||||
targetHeight
|
|
||||||
};
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/types.js
|
|
||||||
function isImageMetadata(src) {
|
|
||||||
return src.fsPath && !("fsPath" in src);
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/utils/url.js
|
|
||||||
var PLACEHOLDER_BASE = "astro://placeholder";
|
|
||||||
function createPlaceholderURL(pathOrUrl) {
|
|
||||||
return new URL(pathOrUrl, PLACEHOLDER_BASE);
|
|
||||||
}
|
|
||||||
function stringifyPlaceholderURL(url) {
|
|
||||||
return url.href.replace(PLACEHOLDER_BASE, "");
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/internal.js
|
|
||||||
var cssFitValues = [
|
|
||||||
"fill",
|
|
||||||
"contain",
|
|
||||||
"cover",
|
|
||||||
"scale-down"
|
|
||||||
];
|
|
||||||
async function getConfiguredImageService() {
|
|
||||||
if (!globalThis?.astroAsset?.imageService) {
|
|
||||||
const { default: service } = await import("./sharp_BQ57rSWH.mjs").catch((e) => {
|
|
||||||
const error = new AstroError(InvalidImageService);
|
|
||||||
error.cause = e;
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
if (!globalThis.astroAsset) globalThis.astroAsset = {};
|
|
||||||
globalThis.astroAsset.imageService = service;
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
return globalThis.astroAsset.imageService;
|
|
||||||
}
|
|
||||||
async function getImage$1(options, imageConfig) {
|
|
||||||
if (!options || typeof options !== "object") throw new AstroError({
|
|
||||||
...ExpectedImageOptions,
|
|
||||||
message: ExpectedImageOptions.message(JSON.stringify(options))
|
|
||||||
});
|
|
||||||
if (typeof options.src === "undefined") throw new AstroError({
|
|
||||||
...ExpectedImage,
|
|
||||||
message: ExpectedImage.message(options.src, "undefined", JSON.stringify(options))
|
|
||||||
});
|
|
||||||
if (isImageMetadata(options)) throw new AstroError(ExpectedNotESMImage);
|
|
||||||
const service = await getConfiguredImageService();
|
|
||||||
const resolvedOptions = {
|
|
||||||
...options,
|
|
||||||
src: await resolveSrc(options.src)
|
|
||||||
};
|
|
||||||
let originalWidth;
|
|
||||||
let originalHeight;
|
|
||||||
if (resolvedOptions.inferSize) {
|
|
||||||
delete resolvedOptions.inferSize;
|
|
||||||
if (isRemoteImage(resolvedOptions.src) && isRemotePath(resolvedOptions.src)) {
|
|
||||||
if (!isRemoteAllowed(resolvedOptions.src, imageConfig)) throw new AstroError({
|
|
||||||
...RemoteImageNotAllowed,
|
|
||||||
message: RemoteImageNotAllowed.message(resolvedOptions.src)
|
|
||||||
});
|
|
||||||
const getRemoteSize = (url) => service.getRemoteSize?.(url, imageConfig) ?? inferRemoteSize$1(url, imageConfig);
|
|
||||||
const result = await getRemoteSize(resolvedOptions.src);
|
|
||||||
resolvedOptions.width ??= result.width;
|
|
||||||
resolvedOptions.height ??= result.height;
|
|
||||||
if (result.format) resolvedOptions.format ??= resolveDefaultOutputFormat(result.format);
|
|
||||||
originalWidth = result.width;
|
|
||||||
originalHeight = result.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const originalFilePath = isESMImportedImage(resolvedOptions.src) ? resolvedOptions.src.fsPath : void 0;
|
|
||||||
const clonedSrc = isESMImportedImage(resolvedOptions.src) ? resolvedOptions.src.clone ?? resolvedOptions.src : resolvedOptions.src;
|
|
||||||
if (isESMImportedImage(clonedSrc)) {
|
|
||||||
originalWidth = clonedSrc.width;
|
|
||||||
originalHeight = clonedSrc.height;
|
|
||||||
}
|
|
||||||
if (originalWidth && originalHeight) {
|
|
||||||
const aspectRatio = originalWidth / originalHeight;
|
|
||||||
if (resolvedOptions.height && !resolvedOptions.width) resolvedOptions.width = Math.round(resolvedOptions.height * aspectRatio);
|
|
||||||
else if (resolvedOptions.width && !resolvedOptions.height) resolvedOptions.height = Math.round(resolvedOptions.width / aspectRatio);
|
|
||||||
else if (!resolvedOptions.width && !resolvedOptions.height) {
|
|
||||||
resolvedOptions.width = originalWidth;
|
|
||||||
resolvedOptions.height = originalHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolvedOptions.src = clonedSrc;
|
|
||||||
const layout = options.layout ?? imageConfig.layout ?? "none";
|
|
||||||
if (resolvedOptions.priority) {
|
|
||||||
resolvedOptions.loading ??= "eager";
|
|
||||||
resolvedOptions.decoding ??= "sync";
|
|
||||||
resolvedOptions.fetchpriority ??= "high";
|
|
||||||
delete resolvedOptions.priority;
|
|
||||||
} else {
|
|
||||||
resolvedOptions.loading ??= "lazy";
|
|
||||||
resolvedOptions.decoding ??= "async";
|
|
||||||
resolvedOptions.fetchpriority ??= void 0;
|
|
||||||
}
|
|
||||||
if (layout !== "none") {
|
|
||||||
resolvedOptions.widths ||= getWidths({
|
|
||||||
width: resolvedOptions.width,
|
|
||||||
layout,
|
|
||||||
originalWidth,
|
|
||||||
breakpoints: imageConfig.breakpoints?.length ? imageConfig.breakpoints : isLocalService(service) ? LIMITED_RESOLUTIONS : DEFAULT_RESOLUTIONS
|
|
||||||
});
|
|
||||||
resolvedOptions.sizes ||= getSizesAttribute({
|
|
||||||
width: resolvedOptions.width,
|
|
||||||
layout
|
|
||||||
});
|
|
||||||
delete resolvedOptions.densities;
|
|
||||||
resolvedOptions["data-astro-image"] = layout;
|
|
||||||
if (resolvedOptions.fit && cssFitValues.includes(resolvedOptions.fit)) resolvedOptions["data-astro-image-fit"] = resolvedOptions.fit;
|
|
||||||
resolvedOptions["data-astro-image-pos"] = (resolvedOptions.position || "center").replace(/\s+/g, "-");
|
|
||||||
}
|
|
||||||
const validatedOptions = service.validateOptions ? await service.validateOptions(resolvedOptions, imageConfig) : resolvedOptions;
|
|
||||||
validatedOptions.format ??= await peekRemoteFormatForStaticEmit(validatedOptions, imageConfig, service);
|
|
||||||
const srcSetTransforms = service.getSrcSet ? await service.getSrcSet(validatedOptions, imageConfig) : [];
|
|
||||||
const lazyImageURLFactory = (getValue) => {
|
|
||||||
let cached = null;
|
|
||||||
return () => cached ??= getValue();
|
|
||||||
};
|
|
||||||
const initialImageURL = await service.getURL(validatedOptions, imageConfig);
|
|
||||||
let lazyImageURL = lazyImageURLFactory(() => initialImageURL);
|
|
||||||
const matchesValidatedTransform = (transform) => transform.width === validatedOptions.width && transform.height === validatedOptions.height && transform.format === validatedOptions.format;
|
|
||||||
let srcSets = await Promise.all(srcSetTransforms.map(async (srcSet) => {
|
|
||||||
return {
|
|
||||||
transform: srcSet.transform,
|
|
||||||
url: matchesValidatedTransform(srcSet.transform) ? initialImageURL : await service.getURL(srcSet.transform, imageConfig),
|
|
||||||
descriptor: srcSet.descriptor,
|
|
||||||
attributes: srcSet.attributes
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
if (isLocalService(service) && globalThis.astroAsset.addStaticImage && !(isRemoteImage(validatedOptions.src) && initialImageURL === validatedOptions.src)) {
|
|
||||||
const propsToHash = service.propertiesToHash ?? DEFAULT_HASH_PROPS;
|
|
||||||
lazyImageURL = lazyImageURLFactory(() => globalThis.astroAsset.addStaticImage(validatedOptions, propsToHash, originalFilePath));
|
|
||||||
srcSets = srcSetTransforms.map((srcSet) => {
|
|
||||||
return {
|
|
||||||
transform: srcSet.transform,
|
|
||||||
url: matchesValidatedTransform(srcSet.transform) ? lazyImageURL() : globalThis.astroAsset.addStaticImage(srcSet.transform, propsToHash, originalFilePath),
|
|
||||||
descriptor: srcSet.descriptor,
|
|
||||||
attributes: srcSet.attributes
|
|
||||||
};
|
|
||||||
});
|
|
||||||
} else if (imageConfig.assetQueryParams) {
|
|
||||||
const imageURLObj = createPlaceholderURL(initialImageURL);
|
|
||||||
imageConfig.assetQueryParams.forEach((value, key) => {
|
|
||||||
imageURLObj.searchParams.set(key, value);
|
|
||||||
});
|
|
||||||
lazyImageURL = lazyImageURLFactory(() => stringifyPlaceholderURL(imageURLObj));
|
|
||||||
srcSets = srcSets.map((srcSet) => {
|
|
||||||
const urlObj = createPlaceholderURL(srcSet.url);
|
|
||||||
imageConfig.assetQueryParams.forEach((value, key) => {
|
|
||||||
urlObj.searchParams.set(key, value);
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
...srcSet,
|
|
||||||
url: stringifyPlaceholderURL(urlObj)
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
rawOptions: resolvedOptions,
|
|
||||||
options: validatedOptions,
|
|
||||||
get src() {
|
|
||||||
return lazyImageURL();
|
|
||||||
},
|
|
||||||
srcSet: {
|
|
||||||
values: srcSets,
|
|
||||||
attribute: srcSets.map((srcSet) => `${srcSet.url} ${srcSet.descriptor}`).join(", ")
|
|
||||||
},
|
|
||||||
attributes: service.getHTMLAttributes !== void 0 ? await service.getHTMLAttributes(validatedOptions, imageConfig) : {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
async function peekRemoteFormatForStaticEmit(options, imageConfig, service) {
|
|
||||||
if (!isRemoteImage(options.src) || !isRemoteAllowed(options.src, imageConfig) || !globalThis.astroAsset?.addStaticImage || !isLocalService(service) || !service.getRemoteSize) return;
|
|
||||||
try {
|
|
||||||
return resolveDefaultOutputFormat((await service.getRemoteSize(options.src, imageConfig)).format);
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Function.prototype.toString.call(Object);
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/components/Image.astro
|
|
||||||
createAstro("https://gabeefran.co");
|
|
||||||
var $$Image = createComponent(async ($$result, $$props, $$slots) => {
|
|
||||||
const Astro2 = $$result.createAstro($$props, $$slots);
|
|
||||||
Astro2.self = $$Image;
|
|
||||||
const props = Astro2.props;
|
|
||||||
if (props.alt === void 0 || props.alt === null) throw new AstroError(ImageMissingAlt);
|
|
||||||
if (typeof props.width === "string") props.width = Number.parseInt(props.width);
|
|
||||||
if (typeof props.height === "string") props.height = Number.parseInt(props.height);
|
|
||||||
if ((props.layout ?? imageConfig.layout ?? "none") !== "none") {
|
|
||||||
props.layout ??= imageConfig.layout;
|
|
||||||
props.fit ??= imageConfig.objectFit ?? "cover";
|
|
||||||
props.position ??= imageConfig.objectPosition ?? "center";
|
|
||||||
} else if (imageConfig.objectFit || imageConfig.objectPosition) {
|
|
||||||
props.fit ??= imageConfig.objectFit;
|
|
||||||
props.position ??= imageConfig.objectPosition;
|
|
||||||
}
|
|
||||||
const image = await getImage(props);
|
|
||||||
const additionalAttributes = {};
|
|
||||||
if (image.srcSet.values.length > 0) additionalAttributes.srcset = image.srcSet.attribute;
|
|
||||||
const { class: className, ...attributes } = {
|
|
||||||
...additionalAttributes,
|
|
||||||
...image.attributes
|
|
||||||
};
|
|
||||||
return renderTemplate`${maybeRenderHead($$result)}<img${addAttribute(image.src, "src")}${spreadAttributes(attributes)}${addAttribute(className, "class")}>`;
|
|
||||||
}, "/home/gabe/src/gabeefran.co-blog/node_modules/astro/components/Image.astro", void 0);
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/components/Picture.astro
|
|
||||||
createAstro("https://gabeefran.co");
|
|
||||||
var $$Picture = createComponent(async ($$result, $$props, $$slots) => {
|
|
||||||
const Astro2 = $$result.createAstro($$props, $$slots);
|
|
||||||
Astro2.self = $$Picture;
|
|
||||||
const defaultFormats = ["webp"];
|
|
||||||
const defaultFallbackFormat = "png";
|
|
||||||
const specialFormatsFallback = [
|
|
||||||
"gif",
|
|
||||||
"svg",
|
|
||||||
"jpg",
|
|
||||||
"jpeg"
|
|
||||||
];
|
|
||||||
const { formats = defaultFormats, pictureAttributes = {}, fallbackFormat, ...props } = Astro2.props;
|
|
||||||
if (props.alt === void 0 || props.alt === null) throw new AstroError(ImageMissingAlt);
|
|
||||||
const scopedStyleClass = props.class?.match(/\bastro-\w{8}\b/)?.[0];
|
|
||||||
if (scopedStyleClass) {
|
|
||||||
if (pictureAttributes.class) pictureAttributes.class = `${pictureAttributes.class} ${scopedStyleClass}`;
|
|
||||||
else pictureAttributes.class = scopedStyleClass;
|
|
||||||
}
|
|
||||||
const useResponsive = (props.layout ?? imageConfig.layout ?? "none") !== "none";
|
|
||||||
if (useResponsive) {
|
|
||||||
props.layout ??= imageConfig.layout;
|
|
||||||
props.fit ??= imageConfig.objectFit ?? "cover";
|
|
||||||
props.position ??= imageConfig.objectPosition ?? "center";
|
|
||||||
} else if (imageConfig.objectFit || imageConfig.objectPosition) {
|
|
||||||
props.fit ??= imageConfig.objectFit;
|
|
||||||
props.position ??= imageConfig.objectPosition;
|
|
||||||
}
|
|
||||||
for (const key in props) if (key.startsWith("data-astro-cid")) pictureAttributes[key] = props[key];
|
|
||||||
const originalSrc = await resolveSrc(props.src);
|
|
||||||
if (props.inferSize && isRemoteImage(originalSrc)) {
|
|
||||||
const remoteSize = await inferRemoteSize(originalSrc);
|
|
||||||
delete props.inferSize;
|
|
||||||
props.width ??= remoteSize.width;
|
|
||||||
props.height ??= remoteSize.height;
|
|
||||||
}
|
|
||||||
const optimizedImages = await Promise.all(formats.map(async (format) => await getImage({
|
|
||||||
...props,
|
|
||||||
src: originalSrc,
|
|
||||||
format,
|
|
||||||
widths: props.widths,
|
|
||||||
densities: props.densities
|
|
||||||
})));
|
|
||||||
const clonedSrc = isESMImportedImage(originalSrc) ? originalSrc.clone ?? originalSrc : originalSrc;
|
|
||||||
let resultFallbackFormat = fallbackFormat ?? defaultFallbackFormat;
|
|
||||||
if (!fallbackFormat && isESMImportedImage(clonedSrc) && specialFormatsFallback.includes(clonedSrc.format)) resultFallbackFormat = clonedSrc.format;
|
|
||||||
const fallbackImage = await getImage({
|
|
||||||
...props,
|
|
||||||
format: resultFallbackFormat,
|
|
||||||
widths: props.widths,
|
|
||||||
densities: props.densities
|
|
||||||
});
|
|
||||||
const imgAdditionalAttributes = {};
|
|
||||||
const sourceAdditionalAttributes = {};
|
|
||||||
if (props.sizes) sourceAdditionalAttributes.sizes = props.sizes;
|
|
||||||
if (fallbackImage.srcSet.values.length > 0) imgAdditionalAttributes.srcset = fallbackImage.srcSet.attribute;
|
|
||||||
const { class: className, ...attributes } = {
|
|
||||||
...imgAdditionalAttributes,
|
|
||||||
...fallbackImage.attributes
|
|
||||||
};
|
|
||||||
return renderTemplate`${maybeRenderHead($$result)}<picture${spreadAttributes(pictureAttributes)}>${Object.entries(optimizedImages).map(([_, image]) => {
|
|
||||||
const srcsetAttribute = props.densities || !props.densities && !props.widths && !useResponsive ? `${image.src}${image.srcSet.values.length > 0 ? ", " + image.srcSet.attribute : ""}` : image.srcSet.attribute;
|
|
||||||
return renderTemplate`<source${addAttribute(srcsetAttribute, "srcset")}${addAttribute(mime.lookup(image.options.format ?? image.src) ?? `image/${image.options.format}`, "type")}${spreadAttributes(sourceAdditionalAttributes)}>`;
|
|
||||||
})}<img${addAttribute(fallbackImage.src, "src")}${spreadAttributes(attributes)}${addAttribute(className, "class")}></picture>`;
|
|
||||||
}, "/home/gabe/src/gabeefran.co-blog/node_modules/astro/components/Picture.astro", void 0);
|
|
||||||
//#endregion
|
|
||||||
//#region \0virtual:astro:assets/fonts/internal
|
|
||||||
var componentDataByCssVariable = /* @__PURE__ */ new Map([]);
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/fonts/core/filter-preloads.js
|
|
||||||
function filterPreloads(data, preload) {
|
|
||||||
if (!preload) return null;
|
|
||||||
if (preload === true) return data;
|
|
||||||
return data.filter(({ weight, style, subset }) => preload.some((p) => {
|
|
||||||
if (p.weight !== void 0 && weight !== void 0 && !checkWeight(p.weight.toString(), weight)) return false;
|
|
||||||
if (p.style !== void 0 && p.style !== style) return false;
|
|
||||||
if (p.subset !== void 0 && p.subset !== subset) return false;
|
|
||||||
return true;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
function checkWeight(input, target) {
|
|
||||||
const trimmedInput = input.trim();
|
|
||||||
if (trimmedInput.includes(" ")) return trimmedInput === target;
|
|
||||||
if (target.includes(" ")) {
|
|
||||||
const [a, b] = target.split(" ");
|
|
||||||
const parsedInput = Number.parseInt(input);
|
|
||||||
return parsedInput >= Number.parseInt(a) && parsedInput <= Number.parseInt(b);
|
|
||||||
}
|
|
||||||
return input === target;
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/components/Font.astro
|
|
||||||
createAstro("https://gabeefran.co");
|
|
||||||
var $$Font = createComponent(($$result, $$props, $$slots) => {
|
|
||||||
const Astro = $$result.createAstro($$props, $$slots);
|
|
||||||
Astro.self = $$Font;
|
|
||||||
const { cssVariable, preload = false } = Astro.props;
|
|
||||||
const data = componentDataByCssVariable.get(cssVariable);
|
|
||||||
if (!data) throw new AstroError({
|
|
||||||
...FontFamilyNotFound,
|
|
||||||
message: FontFamilyNotFound.message(cssVariable)
|
|
||||||
});
|
|
||||||
const filteredPreloadData = filterPreloads(data.preloads, preload);
|
|
||||||
return renderTemplate`<style>${unescapeHTML(data.css)}</style>${filteredPreloadData?.map(({ url, type }) => renderTemplate`<link rel="preload"${addAttribute(url, "href")} as="font"${addAttribute(`font/${type}`, "type")} crossorigin>`)}`;
|
|
||||||
}, "/home/gabe/src/gabeefran.co-blog/node_modules/astro/components/Font.astro", void 0);
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/fonts/infra/ssr-runtime-font-file-url-resolver.js
|
|
||||||
var SsrRuntimeFontFileUrlResolver = class {
|
|
||||||
#urls;
|
|
||||||
constructor({ urls }) {
|
|
||||||
this.#urls = urls;
|
|
||||||
}
|
|
||||||
resolve(url, requestUrl) {
|
|
||||||
if (!this.#urls.has(url)) return null;
|
|
||||||
if (!url.startsWith("/")) return url;
|
|
||||||
if (!requestUrl) throw new AstroError(MissingGetFontFileRequestUrl);
|
|
||||||
return `${requestUrl.origin}${url}`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
new SsrRuntimeFontFileUrlResolver({ urls: /* @__PURE__ */ new Set([]) });
|
|
||||||
//#endregion
|
|
||||||
//#region \0astro:assets
|
|
||||||
var assetQueryParams = void 0;
|
|
||||||
var imageConfig = {
|
|
||||||
"endpoint": { "route": "/_image" },
|
|
||||||
"service": {
|
|
||||||
"entrypoint": "astro/assets/services/sharp",
|
|
||||||
"config": {}
|
|
||||||
},
|
|
||||||
"dangerouslyProcessSVG": false,
|
|
||||||
"domains": [],
|
|
||||||
"remotePatterns": [],
|
|
||||||
"responsiveStyles": false
|
|
||||||
};
|
|
||||||
Object.defineProperty(imageConfig, "assetQueryParams", {
|
|
||||||
value: assetQueryParams,
|
|
||||||
enumerable: false,
|
|
||||||
configurable: true
|
|
||||||
});
|
|
||||||
var inferRemoteSize = async (url) => {
|
|
||||||
return (await getConfiguredImageService()).getRemoteSize?.(url, imageConfig) ?? inferRemoteSize$1(url, imageConfig);
|
|
||||||
};
|
|
||||||
var getImage = async (options) => await getImage$1(options, imageConfig);
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/utils/etag.js
|
|
||||||
var fnv1a52 = (str) => {
|
|
||||||
const len = str.length;
|
|
||||||
let i = 0, t0 = 0, v0 = 8997, t1 = 0, v1 = 33826, t2 = 0, v2 = 40164, t3 = 0, v3 = 52210;
|
|
||||||
while (i < len) {
|
|
||||||
v0 ^= str.charCodeAt(i++);
|
|
||||||
t0 = v0 * 435;
|
|
||||||
t1 = v1 * 435;
|
|
||||||
t2 = v2 * 435;
|
|
||||||
t3 = v3 * 435;
|
|
||||||
t2 += v0 << 8;
|
|
||||||
t3 += v1 << 8;
|
|
||||||
t1 += t0 >>> 16;
|
|
||||||
v0 = t0 & 65535;
|
|
||||||
t2 += t1 >>> 16;
|
|
||||||
v1 = t1 & 65535;
|
|
||||||
v3 = t3 + (t2 >>> 16) & 65535;
|
|
||||||
v2 = t2 & 65535;
|
|
||||||
}
|
|
||||||
return (v3 & 15) * 281474976710656 + v2 * 4294967296 + v1 * 65536 + (v0 ^ v3 >> 4);
|
|
||||||
};
|
|
||||||
var etag = (payload, weak = false) => {
|
|
||||||
return (weak ? "W/\"" : "\"") + fnv1a52(payload).toString(36) + payload.length.toString(36) + "\"";
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/endpoint/loadImage.js
|
|
||||||
async function loadImage(src, headers, imageConfig, isRemote, fetchFn) {
|
|
||||||
try {
|
|
||||||
const res = await fetchWithRedirects({
|
|
||||||
url: src,
|
|
||||||
headers,
|
|
||||||
imageConfig,
|
|
||||||
fetchFn
|
|
||||||
});
|
|
||||||
if (isRemote && !isRemoteAllowed(res.url, imageConfig)) return;
|
|
||||||
if (!res.ok) return;
|
|
||||||
return await res.arrayBuffer();
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
//#region node_modules/astro/dist/assets/endpoint/generic.js
|
|
||||||
var generic_exports = /* @__PURE__ */ __exportAll({ GET: () => GET });
|
|
||||||
var GET = async ({ request }) => {
|
|
||||||
try {
|
|
||||||
const imageService = await getConfiguredImageService();
|
|
||||||
if (!("transform" in imageService)) throw new Error("Configured image service is not a local service");
|
|
||||||
const url = new URL(request.url);
|
|
||||||
const transform = await imageService.parseURL(url, imageConfig);
|
|
||||||
if (!transform?.src) throw new Error("Incorrect transform returned by `parseURL`");
|
|
||||||
let inputBuffer = void 0;
|
|
||||||
const isRemoteImage = isRemotePath(transform.src);
|
|
||||||
if (isRemoteImage && isRemoteAllowed(transform.src, imageConfig) === false) return new Response("Forbidden", { status: 403 });
|
|
||||||
const sourceUrl = new URL(transform.src, url.origin);
|
|
||||||
if (!isRemoteImage && sourceUrl.origin !== url.origin) return new Response("Forbidden", { status: 403 });
|
|
||||||
inputBuffer = await loadImage(sourceUrl, isRemoteImage ? new Headers() : request.headers, imageConfig, isRemoteImage);
|
|
||||||
if (!inputBuffer) return new Response("Not Found", { status: 404 });
|
|
||||||
const { data, format } = await imageService.transform(new Uint8Array(inputBuffer), transform, imageConfig);
|
|
||||||
return new Response(data, {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": mime.lookup(format) ?? `image/${format}`,
|
|
||||||
"Cache-Control": "public, max-age=31536000",
|
|
||||||
ETag: etag(data.toString()),
|
|
||||||
Date: (/* @__PURE__ */ new Date()).toUTCString()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Could not process image request:", err);
|
|
||||||
return new Response("Internal Server Error", { status: 500 });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
//#region \0virtual:astro:page:node_modules/astro/dist/assets/endpoint/generic@_@js
|
|
||||||
var generic___js_exports = /* @__PURE__ */ __exportAll({ page: () => page });
|
|
||||||
var page = () => generic_exports;
|
|
||||||
//#endregion
|
|
||||||
export { resolveDefaultOutputFormat as i, baseService as n, page, parseQuality as r, generic___js_exports as t };
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
//#region \0virtual:astro:actions/noop-entrypoint
|
|
||||||
var server = {};
|
|
||||||
//#endregion
|
|
||||||
export { server };
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
import { n as __exportAll, t as createComponent } from "./compiler_DiXpMM4i.mjs";
|
|
||||||
import { g as renderHead, m as renderTemplate, w as createAstro } from "./jsx-runtime_DseUv9uh.mjs";
|
|
||||||
//#region src/pages/page-card.astro
|
|
||||||
var page_card_exports = /* @__PURE__ */ __exportAll({
|
|
||||||
default: () => $$PageCard,
|
|
||||||
file: () => $$file,
|
|
||||||
prerender: () => false,
|
|
||||||
url: () => $$url
|
|
||||||
});
|
|
||||||
createAstro("https://gabeefran.co");
|
|
||||||
var $$PageCard = createComponent(($$result, $$props, $$slots) => {
|
|
||||||
const Astro = $$result.createAstro($$props, $$slots);
|
|
||||||
Astro.self = $$PageCard;
|
|
||||||
const title = Astro.url.searchParams.get("title") || "gabeefranco";
|
|
||||||
const description = Astro.url.searchParams.get("description");
|
|
||||||
return renderTemplate`<html data-astro-cid-jwkqgd2l><head><meta charset="utf-8"><meta name="robots" content="noindex"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=JetBrains+Mono:wght@600&display=swap" rel="stylesheet">${renderHead($$result)}</head><body data-astro-cid-jwkqgd2l><div class="brand" data-astro-cid-jwkqgd2l><img src="/logo.png" alt="" data-astro-cid-jwkqgd2l><span data-astro-cid-jwkqgd2l>gabeefranco</span></div><h1 class="title" data-astro-cid-jwkqgd2l>${title}</h1>${description && renderTemplate`<p class="description" data-astro-cid-jwkqgd2l>${description}</p>`}</body></html>`;
|
|
||||||
}, "/home/gabe/src/gabeefran.co-blog/src/pages/page-card.astro", void 0);
|
|
||||||
var $$file = "/home/gabe/src/gabeefran.co-blog/src/pages/page-card.astro";
|
|
||||||
var $$url = "/page-card";
|
|
||||||
//#endregion
|
|
||||||
//#region \0virtual:astro:page:src/pages/page-card@_@astro
|
|
||||||
var page = () => page_card_exports;
|
|
||||||
//#endregion
|
|
||||||
export { page };
|
|
||||||
|
|
@ -1,153 +0,0 @@
|
||||||
import { N as NoImageMetadata, Y as UnsupportedImageFormat, Z as AstroError, j as MissingSharp } from "./errors-data_zYnyWRO_.mjs";
|
|
||||||
import { y as detector } from "./path_BQC-SKOp.mjs";
|
|
||||||
import { i as resolveDefaultOutputFormat, n as baseService, r as parseQuality } from "./generic_DKF9tBMu.mjs";
|
|
||||||
//#region node_modules/astro/dist/assets/services/sharp.js
|
|
||||||
var sharp;
|
|
||||||
var qualityTable = {
|
|
||||||
low: 25,
|
|
||||||
mid: 50,
|
|
||||||
high: 80,
|
|
||||||
max: 100
|
|
||||||
};
|
|
||||||
function resolveSharpQuality(quality) {
|
|
||||||
if (!quality) return void 0;
|
|
||||||
const parsedQuality = parseQuality(quality);
|
|
||||||
if (typeof parsedQuality === "number") return parsedQuality;
|
|
||||||
return quality in qualityTable ? qualityTable[quality] : void 0;
|
|
||||||
}
|
|
||||||
function resolveSharpEncoderOptions(transform, inputFormat, serviceConfig = {}) {
|
|
||||||
const quality = resolveSharpQuality(transform.quality);
|
|
||||||
if (transform.format === void 0) return quality === void 0 ? void 0 : { quality };
|
|
||||||
switch (transform.format) {
|
|
||||||
case "jpg":
|
|
||||||
case "jpeg": return {
|
|
||||||
...serviceConfig.jpeg,
|
|
||||||
...quality === void 0 ? {} : { quality }
|
|
||||||
};
|
|
||||||
case "png": return {
|
|
||||||
...serviceConfig.png,
|
|
||||||
...quality === void 0 ? {} : { quality }
|
|
||||||
};
|
|
||||||
case "webp": {
|
|
||||||
const webpOptions = {
|
|
||||||
...serviceConfig.webp,
|
|
||||||
...quality === void 0 ? {} : { quality }
|
|
||||||
};
|
|
||||||
if (inputFormat === "gif") webpOptions.loop ??= 0;
|
|
||||||
return webpOptions;
|
|
||||||
}
|
|
||||||
case "avif": return {
|
|
||||||
...serviceConfig.avif,
|
|
||||||
...quality === void 0 ? {} : { quality }
|
|
||||||
};
|
|
||||||
default: return quality === void 0 ? void 0 : { quality };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function loadSharp() {
|
|
||||||
let sharpImport;
|
|
||||||
try {
|
|
||||||
sharpImport = (await import("sharp")).default;
|
|
||||||
} catch {
|
|
||||||
throw new AstroError(MissingSharp);
|
|
||||||
}
|
|
||||||
sharpImport.cache(false);
|
|
||||||
return sharpImport;
|
|
||||||
}
|
|
||||||
var fitMap = {
|
|
||||||
fill: "fill",
|
|
||||||
contain: "inside",
|
|
||||||
cover: "cover",
|
|
||||||
none: "outside",
|
|
||||||
"scale-down": "inside",
|
|
||||||
outside: "outside",
|
|
||||||
inside: "inside"
|
|
||||||
};
|
|
||||||
var sharp_default = {
|
|
||||||
validateOptions: baseService.validateOptions,
|
|
||||||
getURL: baseService.getURL,
|
|
||||||
parseURL: baseService.parseURL,
|
|
||||||
getHTMLAttributes: baseService.getHTMLAttributes,
|
|
||||||
getSrcSet: baseService.getSrcSet,
|
|
||||||
getRemoteSize: baseService.getRemoteSize,
|
|
||||||
async transform(inputBuffer, transformOptions, config) {
|
|
||||||
if (!sharp) sharp = await loadSharp();
|
|
||||||
const transform = transformOptions;
|
|
||||||
const kernel = config.service.config.kernel;
|
|
||||||
const bufferFormat = detector(inputBuffer);
|
|
||||||
const outputFormat = transform.format ?? resolveDefaultOutputFormat(bufferFormat);
|
|
||||||
if (outputFormat === "svg") {
|
|
||||||
if (bufferFormat && bufferFormat !== "svg") {
|
|
||||||
console.warn(`\u26A0\uFE0F Astro expected an SVG for "${transform.src}" but the source is ${bufferFormat}. Passing it through as ${bufferFormat} instead.`);
|
|
||||||
return {
|
|
||||||
data: inputBuffer,
|
|
||||||
format: bufferFormat
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
data: inputBuffer,
|
|
||||||
format: "svg"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!bufferFormat) throw new AstroError({
|
|
||||||
...NoImageMetadata,
|
|
||||||
message: NoImageMetadata.message(transform.src)
|
|
||||||
});
|
|
||||||
if (bufferFormat === "svg" && !config.dangerouslyProcessSVG) throw new AstroError({
|
|
||||||
...UnsupportedImageFormat,
|
|
||||||
message: `SVG image processing is disabled, but the source for "${transform.src}" is an SVG. Pass it through unchanged by setting \`format="svg"\` on the component, or set \`image.dangerouslyProcessSVG: true\` to rasterize SVG sources.`
|
|
||||||
});
|
|
||||||
const result = sharp(inputBuffer, {
|
|
||||||
failOn: "none",
|
|
||||||
pages: -1,
|
|
||||||
limitInputPixels: config.service.config.limitInputPixels
|
|
||||||
});
|
|
||||||
result.rotate();
|
|
||||||
if (transform.width && transform.height) {
|
|
||||||
const fit = transform.fit ? fitMap[transform.fit] ?? "inside" : void 0;
|
|
||||||
result.resize({
|
|
||||||
width: Math.round(transform.width),
|
|
||||||
height: Math.round(transform.height),
|
|
||||||
kernel,
|
|
||||||
fit,
|
|
||||||
position: transform.position,
|
|
||||||
withoutEnlargement: true
|
|
||||||
});
|
|
||||||
} else if (transform.height && !transform.width) result.resize({
|
|
||||||
height: Math.round(transform.height),
|
|
||||||
withoutEnlargement: true,
|
|
||||||
kernel
|
|
||||||
});
|
|
||||||
else if (transform.width) result.resize({
|
|
||||||
width: Math.round(transform.width),
|
|
||||||
withoutEnlargement: true,
|
|
||||||
kernel
|
|
||||||
});
|
|
||||||
if (transform.background) result.flatten({ background: transform.background });
|
|
||||||
const encoderOptions = resolveSharpEncoderOptions({
|
|
||||||
format: outputFormat,
|
|
||||||
quality: transform.quality
|
|
||||||
}, bufferFormat, config.service.config);
|
|
||||||
if (outputFormat === "webp") result.webp(encoderOptions);
|
|
||||||
else if (outputFormat === "png") result.png(encoderOptions);
|
|
||||||
else if (outputFormat === "avif") result.avif(encoderOptions);
|
|
||||||
else if (outputFormat === "jpeg" || outputFormat === "jpg") result.jpeg(encoderOptions);
|
|
||||||
else result.toFormat(outputFormat, encoderOptions);
|
|
||||||
let data;
|
|
||||||
let info;
|
|
||||||
try {
|
|
||||||
({data, info} = await result.toBuffer({ resolveWithObject: true }));
|
|
||||||
} catch {
|
|
||||||
console.warn(`\u26A0\uFE0F Astro could not optimize image "${transform.src}". Sharp doesn't support this format. The image will be used unoptimized. Consider converting to WebP or placing in the public/ folder.`);
|
|
||||||
return {
|
|
||||||
data: inputBuffer,
|
|
||||||
format: bufferFormat
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
data: "buffer" in data && data.buffer instanceof SharedArrayBuffer ? new Uint8Array(data) : data,
|
|
||||||
format: info.format
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//#endregion
|
|
||||||
export { sharp_default as default, resolveSharpEncoderOptions };
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
import { n as __exportAll, t as createComponent } from "./compiler_DiXpMM4i.mjs";
|
|
||||||
import { g as renderHead, m as renderTemplate, w as createAstro } from "./jsx-runtime_DseUv9uh.mjs";
|
|
||||||
//#region src/pages/social-card.astro
|
|
||||||
var social_card_exports = /* @__PURE__ */ __exportAll({
|
|
||||||
default: () => $$SocialCard,
|
|
||||||
file: () => $$file,
|
|
||||||
prerender: () => false,
|
|
||||||
url: () => $$url
|
|
||||||
});
|
|
||||||
createAstro("https://gabeefran.co");
|
|
||||||
var $$SocialCard = createComponent(($$result, $$props, $$slots) => {
|
|
||||||
const Astro = $$result.createAstro($$props, $$slots);
|
|
||||||
Astro.self = $$SocialCard;
|
|
||||||
const title = Astro.url.searchParams.get("title") || "Untitled";
|
|
||||||
const description = Astro.url.searchParams.get("description");
|
|
||||||
const author = Astro.url.searchParams.get("author");
|
|
||||||
return renderTemplate`<html data-astro-cid-3fpsgztt><head><meta charset="utf-8"><meta name="robots" content="noindex"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=JetBrains+Mono:wght@600&display=swap" rel="stylesheet">${renderHead($$result)}</head><body data-astro-cid-3fpsgztt><div class="brand" data-astro-cid-3fpsgztt><img src="/logo.png" alt="" data-astro-cid-3fpsgztt><span data-astro-cid-3fpsgztt>gabeefranco</span></div><h1 class="title" data-astro-cid-3fpsgztt>${title}</h1>${description && renderTemplate`<p class="description" data-astro-cid-3fpsgztt>${description}</p>`}${author && renderTemplate`<p class="author" data-astro-cid-3fpsgztt>${author}</p>`}</body></html>`;
|
|
||||||
}, "/home/gabe/src/gabeefran.co-blog/src/pages/social-card.astro", void 0);
|
|
||||||
var $$file = "/home/gabe/src/gabeefran.co-blog/src/pages/social-card.astro";
|
|
||||||
var $$url = "/social-card";
|
|
||||||
//#endregion
|
|
||||||
//#region \0virtual:astro:page:src/pages/social-card@_@astro
|
|
||||||
var page = () => social_card_exports;
|
|
||||||
//#endregion
|
|
||||||
export { page };
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
import { Fragment, createElement } from "react";
|
|
||||||
import { DOCUMENT_NODE, ELEMENT_NODE, TEXT_NODE, parse } from "ultrahtml";
|
|
||||||
//#region node_modules/@astrojs/react/dist/vnode-children.js
|
|
||||||
var ids = 0;
|
|
||||||
function convert(children) {
|
|
||||||
let doc = parse(children.toString().trim());
|
|
||||||
let id = ids++;
|
|
||||||
let key = 0;
|
|
||||||
function createReactElementFromNode(node) {
|
|
||||||
const childVnodes = Array.isArray(node.children) && node.children.length ? node.children.map((child) => createReactElementFromNode(child)).filter(Boolean) : void 0;
|
|
||||||
if (node.type === DOCUMENT_NODE) return createElement(Fragment, {}, childVnodes);
|
|
||||||
else if (node.type === ELEMENT_NODE) {
|
|
||||||
const { class: className, ...props } = node.attributes;
|
|
||||||
return createElement(node.name, {
|
|
||||||
...props,
|
|
||||||
className,
|
|
||||||
key: `${id}-${key++}`
|
|
||||||
}, childVnodes);
|
|
||||||
} else if (node.type === TEXT_NODE) return node.value.trim() ? node.value : void 0;
|
|
||||||
}
|
|
||||||
return createReactElementFromNode(doc).props.children;
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
export { convert as default };
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
//#region \0noop-middleware
|
|
||||||
var onRequest = (_, next) => next();
|
|
||||||
//#endregion
|
|
||||||
export { onRequest };
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"type": "module"
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="alternate icon" href="/favicon.ico"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="canonical" href="https://gabeefran.co/404/"><link rel="alternate" hreflang="en" href="https://gabeefran.co/404/"><link rel="alternate" hreflang="pt-BR" href="https://gabeefran.co/pt/404/"><meta name="generator" content="Astro v7.2.10"><title>Page not found — gabeefranco</title><meta name="description" content="There’s nothing here. Let’s get you back home."><meta property="og:type" content="website"><meta property="og:title" content="Page not found — gabeefranco"><meta property="og:description" content="There’s nothing here. Let’s get you back home."><meta property="og:url" content="https://gabeefran.co/404/"><meta property="og:image" content="https://gabeefran.co/logo.png"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="Page not found — gabeefranco"><meta name="twitter:description" content="There’s nothing here. Let’s get you back home."><meta name="twitter:image" content="https://gabeefran.co/logo.png"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400..700;1,400..700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"><script>
|
|
||||||
// Runs before paint to avoid a flash of the wrong theme.
|
|
||||||
(function () {
|
|
||||||
const stored = localStorage.getItem('theme');
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
const dark = stored ? stored === 'dark' : prefersDark;
|
|
||||||
document.documentElement.classList.toggle('dark', dark);
|
|
||||||
})();
|
|
||||||
</script><link rel="stylesheet" href="/_astro/BaseLayout.z2Ysa6px.css"><script>window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };
|
|
||||||
var script = document.createElement('script');
|
|
||||||
script.defer = true;
|
|
||||||
script.src = '/_vercel/insights/script.js';
|
|
||||||
var head = document.querySelector('head');
|
|
||||||
head.appendChild(script);
|
|
||||||
</script></head><body class="flex min-h-screen flex-col bg-bg font-sans text-text antialiased selection:bg-primary selection:text-white"><header class="sticky top-0 z-40 border-b border-border/80 bg-bg/85 backdrop-blur"><div class="flex flex-col justify-center gap-4 px-4 py-3 sm:px-6"><div class="flex flex-row justify-between items-end"><a href="/" class="flex hover:underline shrink-0 items-center gap-2.5" aria-label="gabeefranco"><img src="/logo.png" class="h-4"><span class="font-mono text-[15px] font-semibold tracking-tight text-text">gabeefranco</span></a><div class="ml-10 flex sm:hidden justify-end gap-3"><a href="/pt/404/" hreflang="pt" title="Switch language" aria-label="Switch language: Português" class="text-accent max-w-fit font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep">Switch language: Português</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Toggle color theme" title="Toggle color theme">Toggle color theme</button><script type="module">function e(){document.querySelectorAll(`.theme-toggle`).forEach(e=>{e.addEventListener(`click`,function(){let e=document.documentElement.classList.toggle(`dark`);localStorage.setItem(`theme`,e?`dark`:`light`)})})}e(),document.addEventListener(`astro:after-swap`,e);</script></div><div class="hidden sm:flex items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Home</a><a href="/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">About</a></nav><a href="/pt/404/" hreflang="pt" title="Switch language" aria-label="Switch language: Português" class="text-accent max-w-fit py-1 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep ">Switch language: Português</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Toggle color theme" title="Toggle color theme">Toggle color theme</button></div></div><div class="flex justify-end sm:hidden items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Home</a><a href="/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">About</a></nav></div></div></header><main class="flex-1"><section class="mx-10 flex flex-col items-start px-4 py-24 sm:px-6 font-mono"><p class="font-mono text-sm text-primary">404</p><div data-notfound-lang="en" class><h1 class="mt-2 text-3xl font-bold tracking-tight text-text">Page not found</h1><p class="mt-3 text-text-muted">There’s nothing here. Let’s get you back home.</p><a href="/" class="mt-4 inline-block underline text-primary hover:text-primary-hover font-bold">Back to home</a></div><div data-notfound-lang="pt" class="hidden"><h1 class="mt-2 text-3xl font-bold tracking-tight text-text">Página não encontrada</h1><p class="mt-3 text-text-muted">Não há nada aqui. Vamos te levar de volta para o início.</p><a href="/pt" class="mt-4 inline-block underline text-primary hover:text-primary-hover font-bold">Voltar para o início</a></div></section></main><footer class="border-t border-border/80 font-mono"><div class="mx-auto flex max-w-3xl flex-col gap-2 px-4 py-8 text-sm text-text-muted sm:flex-row sm:items-center sm:justify-between sm:px-6"><p>© 2026 <span class="font-mono text-text">gabeefranco</span> · All rights reserved.</p><div class="flex items-center gap-4"><span class="hidden sm:inline">Written by hand in Porto Alegre.</span><a href="https://github.com/gabeefranco" target="_blank" rel="noopener noreferrer me" class="transition-colors hover:text-primary">GitHub</a><a href="https://github.com/gabeefranco/gabeefran.co-blog" target="_blank" rel="noopener noreferrer" class="transition-colors hover:text-primary">Source</a></div></div></footer></body></html><script>
|
|
||||||
(function () {
|
|
||||||
if (location.pathname.replace(/\/$/, '').startsWith('/pt') === false) return;
|
|
||||||
document.documentElement.lang = 'pt';
|
|
||||||
document.querySelectorAll('[data-notfound-lang]').forEach(function (el) {
|
|
||||||
el.classList.toggle('hidden', el.getAttribute('data-notfound-lang') !== 'pt');
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
|
@ -1,38 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="bowlGrad" x1="0%" y1="0%" x2="100%" y2="60%">
|
|
||||||
<stop offset="0%" stop-color="#F2600D"/>
|
|
||||||
<stop offset="55%" stop-color="#C7350B"/>
|
|
||||||
<stop offset="100%" stop-color="#7A1204"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="fryGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
||||||
<stop offset="0%" stop-color="#FFD24C"/>
|
|
||||||
<stop offset="100%" stop-color="#E08A00"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="fryShade" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
||||||
<stop offset="0%" stop-color="#E8B23A"/>
|
|
||||||
<stop offset="100%" stop-color="#A65E00"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<path d="M 13.1,122 L 14.5,97.4 L 19.5,100.4 L 18.9,122 Z" fill="url(#fryShade)"/>
|
|
||||||
<path d="M 22.4,122 L 21.0,73.6 L 27.7,76.6 L 28.2,122 Z" fill="url(#fryGrad)"/>
|
|
||||||
<path d="M 31.8,122 L 33.2,91.2 L 38.1,94.2 L 37.6,122 Z" fill="url(#fryGrad)"/>
|
|
||||||
<path d="M 41.1,122 L 39.7,67.4 L 46.3,70.4 L 46.9,122 Z" fill="url(#fryShade)"/>
|
|
||||||
<path d="M 50.4,122 L 51.8,85.0 L 56.8,88.0 L 56.2,122 Z" fill="url(#fryGrad)"/>
|
|
||||||
<path d="M 59.8,122 L 58.4,78.0 L 65.0,81.0 L 65.6,122 Z" fill="url(#fryGrad)"/>
|
|
||||||
<path d="M 69.1,122 L 70.5,58.6 L 75.5,61.6 L 74.9,122 Z" fill="url(#fryShade)"/>
|
|
||||||
<path d="M 78.4,122 L 77.0,71.0 L 83.7,74.0 L 84.2,122 Z" fill="url(#fryGrad)"/>
|
|
||||||
<path d="M 87.8,122 L 89.2,44.6 L 94.1,47.6 L 93.6,122 Z" fill="url(#fryGrad)"/>
|
|
||||||
<path d="M 97.1,122 L 95.7,64.8 L 102.3,67.8 L 102.9,122 Z" fill="url(#fryShade)"/>
|
|
||||||
<path d="M 106.4,122 L 107.8,38.4 L 112.8,41.4 L 112.2,122 Z" fill="url(#fryGrad)"/>
|
|
||||||
<path d="M 115.8,122 L 114.4,60.4 L 121.0,63.4 L 121.6,122 Z" fill="url(#fryGrad)"/>
|
|
||||||
<path d="M 125.1,122 L 126.5,69.2 L 131.5,72.2 L 130.9,122 Z" fill="url(#fryShade)"/>
|
|
||||||
<path d="M 134.4,122 L 133.0,51.6 L 139.7,54.6 L 140.2,122 Z" fill="url(#fryGrad)"/>
|
|
||||||
<path d="M 143.8,122 L 145.2,78.0 L 150.1,81.0 L 149.6,122 Z" fill="url(#fryGrad)"/>
|
|
||||||
<path d="M 153.1,122 L 151.7,63.9 L 158.3,66.9 L 158.9,122 Z" fill="url(#fryShade)"/>
|
|
||||||
<path d="M 162.4,122 L 163.8,86.8 L 168.8,89.8 L 168.2,122 Z" fill="url(#fryGrad)"/>
|
|
||||||
<path d="M 171.8,122 L 170.4,76.2 L 177.0,79.2 L 177.6,122 Z" fill="url(#fryGrad)"/>
|
|
||||||
<path d="M 181.1,122 L 182.5,95.6 L 187.5,98.6 L 186.9,122 Z" fill="url(#fryShade)"/>
|
|
||||||
|
|
||||||
<path d="M 6,122 Q 100,172 194,122 Q 178,110 100,110 Q 22,110 6,122 Z" fill="url(#bowlGrad)"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 170 KiB |
|
|
@ -1,19 +0,0 @@
|
||||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="alternate icon" href="/favicon.ico"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="canonical" href="https://gabeefran.co/posts/hello-world/"><link rel="alternate" hreflang="en" href="https://gabeefran.co/posts/hello-world/"><link rel="alternate" hreflang="pt-BR" href="https://gabeefran.co/pt/posts/hello-world/"><meta name="generator" content="Astro v7.2.10"><title>This blog's Hello World (or whatever you want to call this site) — gabeefranco</title><meta name="description" content="A short note on why this exists and what I plan to write about here."><meta property="og:type" content="website"><meta property="og:title" content="This blog's Hello World (or whatever you want to call this site) — gabeefranco"><meta property="og:description" content="A short note on why this exists and what I plan to write about here."><meta property="og:url" content="https://gabeefran.co/posts/hello-world/"><meta property="og:image" content="https://gabeefran.co/social-cards/en/hello-world.png"><meta property="og:image:width" content="1200"><meta property="og:image:height" content="630"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="This blog's Hello World (or whatever you want to call this site) — gabeefranco"><meta name="twitter:description" content="A short note on why this exists and what I plan to write about here."><meta name="twitter:image" content="https://gabeefran.co/social-cards/en/hello-world.png"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400..700;1,400..700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"><script>
|
|
||||||
// Runs before paint to avoid a flash of the wrong theme.
|
|
||||||
(function () {
|
|
||||||
const stored = localStorage.getItem('theme');
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
const dark = stored ? stored === 'dark' : prefersDark;
|
|
||||||
document.documentElement.classList.toggle('dark', dark);
|
|
||||||
})();
|
|
||||||
</script><link rel="stylesheet" href="/_astro/BaseLayout.z2Ysa6px.css"><script>window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };
|
|
||||||
var script = document.createElement('script');
|
|
||||||
script.defer = true;
|
|
||||||
script.src = '/_vercel/insights/script.js';
|
|
||||||
var head = document.querySelector('head');
|
|
||||||
head.appendChild(script);
|
|
||||||
</script></head><body class="flex min-h-screen flex-col bg-bg font-sans text-text antialiased selection:bg-primary selection:text-white"><header class="sticky top-0 z-40 border-b border-border/80 bg-bg/85 backdrop-blur"><div class="flex flex-col justify-center gap-4 px-4 py-3 sm:px-6"><div class="flex flex-row justify-between items-end"><a href="/" class="flex hover:underline shrink-0 items-center gap-2.5" aria-label="gabeefranco"><img src="/logo.png" class="h-4"><span class="font-mono text-[15px] font-semibold tracking-tight text-text">gabeefranco</span></a><div class="ml-10 flex sm:hidden justify-end gap-3"><a href="/pt/posts/hello-world/" hreflang="pt" title="Switch language" aria-label="Switch language: Português" class="text-accent max-w-fit font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep">Switch language: Português</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Toggle color theme" title="Toggle color theme">Toggle color theme</button><script type="module">function e(){document.querySelectorAll(`.theme-toggle`).forEach(e=>{e.addEventListener(`click`,function(){let e=document.documentElement.classList.toggle(`dark`);localStorage.setItem(`theme`,e?`dark`:`light`)})})}e(),document.addEventListener(`astro:after-swap`,e);</script></div><div class="hidden sm:flex items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Home</a><a href="/posts" aria-current="page" class="px-2 py-1 text-sm font-medium font-mono underline text-primary hover:text-primary-hover">Posts</a><a href="/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">About</a></nav><a href="/pt/posts/hello-world/" hreflang="pt" title="Switch language" aria-label="Switch language: Português" class="text-accent max-w-fit py-1 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep ">Switch language: Português</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Toggle color theme" title="Toggle color theme">Toggle color theme</button></div></div><div class="flex justify-end sm:hidden items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Home</a><a href="/posts" aria-current="page" class="px-2 py-1 text-sm font-medium font-mono underline text-primary hover:text-primary-hover">Posts</a><a href="/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">About</a></nav></div></div></header><main class="flex-1"><article class="mx-10 my-10 font-mono"><a href="/posts" class="underline inline-flex items-center gap-1.5 text-sm font-medium text-text-muted hover:text-primary"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4"><path d="M15 18l-6-6 6-6"></path></svg>Back to posts</a><header class="mt-6 border-b border-border pb-6"><div class="flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-text-muted"><time datetime="2026-09-22T00:00:00.000Z">September 21, 2026</time><span aria-hidden="true">·</span><span>2 min read</span></div><h1 class="mt-3 text-3xl font-bold tracking-tight text-text sm:text-4xl">This blog's Hello World (or whatever you want to call this site)</h1><p class="mt-3 text-lg text-text-muted">A short note on why this exists and what I plan to write about here.</p><div class="mt-4 flex flex-wrap gap-2"><a href="/tags/meta" class=" text-xs font-medium text-text-muted underline hover:text-accent">#meta</a><a href="/tags/blogging" class=" text-xs font-medium text-text-muted underline hover:text-accent">#blogging</a></div></header><div class="prose prose-neutral mt-8 max-w-none prose-headings:font-semibold prose-headings:tracking-tight prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-code:before:content-none prose-code:after:content-none prose-img:rounded-xl dark:prose-invert"><p>I’ve always had a creative side, as anyone who knows me can tell you. Sometimes it gets tucked away somewhere obscure, in the back of a drawer, waiting years to be used. And sometimes I make really good use of it.
|
|
||||||
I’ve always loved writing, and I’ve wanted a place of my own to publish my thoughts for a long time. Only the ones I’d want to be public, of course. Nobody wants to hand over the entire inner workings of their brain just like that, for free. Still, I think some of my intellectual adventures deserve a bit of exposure, especially since, without someone to judge them, I’ll never know if they were worth it.</p>
|
|
||||||
<p>One of my inspirations for creating a site/blog was <a href="https://instagram.com/akitaonrails">Fábio Akita</a>. I’ve been following him for a long time, and it was through the videos on <a href="https://youtube.com/akitaonrails">his YouTube channel</a> that I started studying computer science, before I even got into college. Even though the videos are genuinely great, teaching things that are hard to learn any other way, after a while of binge-watching them I found his blog, <a href="https://akitaonrails.com">akitaonrails.com</a>. I thought it was super cool, and I liked the idea of having a personal place to share your opinions and the things you’ve learned.</p>
|
|
||||||
<p>Akita is a seasoned guy who’s been in the industry for years. Me? Just a student slightly ahead of the material in my CS program. To people my age, this whole writing thing, having a blog, sounds like something old people do. But, like I said in my <a href="https://gabeefran.co/posts/spotify-complaining">last post</a>, I like the idea of doing things the old-fashioned way every now and then. The word <em>“Blog”</em> sounds so 2010, but I don’t know what else I’d call this crappy site you’re reading. I guess <em>“blog”</em> it is. Good thing I’ve been practicing caring less about what other people think, and look at that: it’s working!</p>
|
|
||||||
<p>Anyway, call it whatever you want, I think it’s worth posting something here every now and then. Even if nobody’s interested, whatever! Then let it just be my public folder of notes. Just the fact that I opened the drawer and pulled my creativity out from the back of it already makes it all worth it.</p></div></article></main><footer class="border-t border-border/80 font-mono"><div class="mx-auto flex max-w-3xl flex-col gap-2 px-4 py-8 text-sm text-text-muted sm:flex-row sm:items-center sm:justify-between sm:px-6"><p>© 2026 <span class="font-mono text-text">gabeefranco</span> · All rights reserved.</p><div class="flex items-center gap-4"><span class="hidden sm:inline">Written by hand in Porto Alegre.</span><a href="https://github.com/gabeefranco" target="_blank" rel="noopener noreferrer me" class="transition-colors hover:text-primary">GitHub</a><a href="https://github.com/gabeefranco/gabeefran.co-blog" target="_blank" rel="noopener noreferrer" class="transition-colors hover:text-primary">Source</a></div></div></footer></body></html>
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
<!DOCTYPE html><html lang="pt"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="alternate icon" href="/favicon.ico"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="canonical" href="https://gabeefran.co/pt/posts/hello-world/"><link rel="alternate" hreflang="en" href="https://gabeefran.co/posts/hello-world/"><link rel="alternate" hreflang="pt-BR" href="https://gabeefran.co/pt/posts/hello-world/"><meta name="generator" content="Astro v7.2.10"><title>O Hello World desse blog (ou seja lá como você queira chamar esse site) — gabeefranco</title><meta name="description" content="Uma nota curta sobre por que isso existe e sobre o que pretendo escrever aqui."><meta property="og:type" content="website"><meta property="og:title" content="O Hello World desse blog (ou seja lá como você queira chamar esse site) — gabeefranco"><meta property="og:description" content="Uma nota curta sobre por que isso existe e sobre o que pretendo escrever aqui."><meta property="og:url" content="https://gabeefran.co/pt/posts/hello-world/"><meta property="og:image" content="https://gabeefran.co/social-cards/pt/hello-world.png"><meta property="og:image:width" content="1200"><meta property="og:image:height" content="630"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="O Hello World desse blog (ou seja lá como você queira chamar esse site) — gabeefranco"><meta name="twitter:description" content="Uma nota curta sobre por que isso existe e sobre o que pretendo escrever aqui."><meta name="twitter:image" content="https://gabeefran.co/social-cards/pt/hello-world.png"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400..700;1,400..700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"><script>
|
|
||||||
// Runs before paint to avoid a flash of the wrong theme.
|
|
||||||
(function () {
|
|
||||||
const stored = localStorage.getItem('theme');
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
const dark = stored ? stored === 'dark' : prefersDark;
|
|
||||||
document.documentElement.classList.toggle('dark', dark);
|
|
||||||
})();
|
|
||||||
</script><link rel="stylesheet" href="/_astro/BaseLayout.z2Ysa6px.css"><script>window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };
|
|
||||||
var script = document.createElement('script');
|
|
||||||
script.defer = true;
|
|
||||||
script.src = '/_vercel/insights/script.js';
|
|
||||||
var head = document.querySelector('head');
|
|
||||||
head.appendChild(script);
|
|
||||||
</script></head><body class="flex min-h-screen flex-col bg-bg font-sans text-text antialiased selection:bg-primary selection:text-white"><header class="sticky top-0 z-40 border-b border-border/80 bg-bg/85 backdrop-blur"><div class="flex flex-col justify-center gap-4 px-4 py-3 sm:px-6"><div class="flex flex-row justify-between items-end"><a href="/pt" class="flex hover:underline shrink-0 items-center gap-2.5" aria-label="gabeefranco"><img src="/logo.png" class="h-4"><span class="font-mono text-[15px] font-semibold tracking-tight text-text">gabeefranco</span></a><div class="ml-10 flex sm:hidden justify-end gap-3"><a href="/posts/hello-world/" hreflang="en" title="Mudar idioma" aria-label="Mudar idioma: English" class="text-accent max-w-fit font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep">Mudar idioma: English</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Alternar tema" title="Alternar tema">Alternar tema</button><script type="module">function e(){document.querySelectorAll(`.theme-toggle`).forEach(e=>{e.addEventListener(`click`,function(){let e=document.documentElement.classList.toggle(`dark`);localStorage.setItem(`theme`,e?`dark`:`light`)})})}e(),document.addEventListener(`astro:after-swap`,e);</script></div><div class="hidden sm:flex items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/pt" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Início</a><a href="/pt/posts" aria-current="page" class="px-2 py-1 text-sm font-medium font-mono underline text-primary hover:text-primary-hover">Posts</a><a href="/pt/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Sobre</a></nav><a href="/posts/hello-world/" hreflang="en" title="Mudar idioma" aria-label="Mudar idioma: English" class="text-accent max-w-fit py-1 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep ">Mudar idioma: English</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Alternar tema" title="Alternar tema">Alternar tema</button></div></div><div class="flex justify-end sm:hidden items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/pt" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Início</a><a href="/pt/posts" aria-current="page" class="px-2 py-1 text-sm font-medium font-mono underline text-primary hover:text-primary-hover">Posts</a><a href="/pt/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Sobre</a></nav></div></div></header><main class="flex-1"><article class="mx-10 my-10 font-mono"><a href="/pt/posts" class="underline inline-flex items-center gap-1.5 text-sm font-medium text-text-muted hover:text-primary"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4"><path d="M15 18l-6-6 6-6"></path></svg>Voltar para os posts</a><header class="mt-6 border-b border-border pb-6"><div class="flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-text-muted"><time datetime="2026-09-22T00:00:00.000Z">21 de setembro de 2026</time><span aria-hidden="true">·</span><span>2 min de leitura</span></div><h1 class="mt-3 text-3xl font-bold tracking-tight text-text sm:text-4xl">O Hello World desse blog (ou seja lá como você queira chamar esse site)</h1><p class="mt-3 text-lg text-text-muted">Uma nota curta sobre por que isso existe e sobre o que pretendo escrever aqui.</p><div class="mt-4 flex flex-wrap gap-2"><a href="/pt/tags/meta" class=" text-xs font-medium text-text-muted underline hover:text-accent">#meta</a><a href="/pt/tags/blogging" class=" text-xs font-medium text-text-muted underline hover:text-accent">#blogging</a></div></header><div class="prose prose-neutral mt-8 max-w-none prose-headings:font-semibold prose-headings:tracking-tight prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-code:before:content-none prose-code:after:content-none prose-img:rounded-xl dark:prose-invert"><p>Eu sempre tive um lado criativo, quem me conhece sabe. Às vezes ele fica guardado em algum lugar obscuro, no fundo da gaveta, esperando anos pra ser utilizado. E às vezes eu me aproveito dele muito bem.
|
|
||||||
Sempre gostei muito de escrever, e faz tempo que queria ter um lugar só meu pra publicar meus pensamentos. Mas só aqueles que eu gostaria que fossem públicos, claro. Ninguém quer entregar todo o funcionamento interno do seu cérebro assim, de graça. Ainda assim, acho que algumas das minhas aventuras intelectuais merecem um pouco de exposição, até porque, sem alguém pra julgar, nunca saberei se valeram a pena.</p>
|
|
||||||
<p>Uma das minhas inspirações pra criar um site/blog foi o <a href="https://instagram.com/akitaonrails">Fábio Akita</a>. Eu acompanho ele há muito tempo, e foi por meio dos vídeos do <a href="https://youtube.com/akitaonrails">canal do YouTube dele</a> que eu comecei a estudar sobre computação, antes de entrar na faculdade. Apesar dos vídeos serem realmente muito bons, fornecendo aprendizados difíceis de obter de outra forma, depois de um tempo maratonando os vídeos eu descobri o blog dele, <a href="https://akitaonrails.com">akitaonrails.com</a>. Eu achei muito massa, e gostei da ideia de se ter um lugar pessoal onde se expõem opiniões e aprendizados.</p>
|
|
||||||
<p>O Akita é um cara com experiência, que vive há anos na indústria. Eu, um mero estudante levemente adiantado no conteúdo da faculdade de computação. Esse negócio de escrever, ter um blog, parece coisa de gente velha pras pessoas da minha geração. Mas, como falei no <a href="https://gabeefran.co/pt/posts/spotify-complaining">último post</a>, eu gosto da ideia de fazer coisas à moda antiga de vez em quando. O termo <em>“Blog”</em> soa muito 2010, mas não sei que outro nome daria a esta porcaria deste site que você está lendo. Acho que vai ser <em>“blog”</em> mesmo. Bem que eu tenho treinado pra ligar menos pra opinião dos outros, e olha só: tá funcionando!</p>
|
|
||||||
<p>Enfim, chame como você quiser, eu acho que vale a pena publicar algo de vez em quando por aqui. Mesmo que ninguém esteja interessado, tanto faz! Que seja apenas a minha pasta pública de notas, então. Só de eu ter aberto a gaveta e tirado de lá do fundo a minha criatividade, considero que já valeu a pena.</p></div></article></main><footer class="border-t border-border/80 font-mono"><div class="mx-auto flex max-w-3xl flex-col gap-2 px-4 py-8 text-sm text-text-muted sm:flex-row sm:items-center sm:justify-between sm:px-6"><p>© 2026 <span class="font-mono text-text">gabeefranco</span> · Todos os direitos reservados.</p><div class="flex items-center gap-4"><span class="hidden sm:inline">Escrito à mão em Porto Alegre.</span><a href="https://github.com/gabeefranco" target="_blank" rel="noopener noreferrer me" class="transition-colors hover:text-primary">GitHub</a><a href="https://github.com/gabeefranco/gabeefran.co-blog" target="_blank" rel="noopener noreferrer" class="transition-colors hover:text-primary">Código-fonte</a></div></div></footer></body></html>
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
<!DOCTYPE html><html lang="pt"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="alternate icon" href="/favicon.ico"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="canonical" href="https://gabeefran.co/pt/tags/blogging/"><link rel="alternate" hreflang="en" href="https://gabeefran.co/tags/blogging/"><link rel="alternate" hreflang="pt-BR" href="https://gabeefran.co/pt/tags/blogging/"><meta name="generator" content="Astro v7.2.10"><title>#blogging — gabeefranco</title><meta name="description" content="Tudo que já escrevi com a tag #blogging."><meta property="og:type" content="website"><meta property="og:title" content="#blogging — gabeefranco"><meta property="og:description" content="Tudo que já escrevi com a tag #blogging."><meta property="og:url" content="https://gabeefran.co/pt/tags/blogging/"><meta property="og:image" content="https://gabeefran.co/logo.png"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="#blogging — gabeefranco"><meta name="twitter:description" content="Tudo que já escrevi com a tag #blogging."><meta name="twitter:image" content="https://gabeefran.co/logo.png"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400..700;1,400..700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"><script>
|
|
||||||
// Runs before paint to avoid a flash of the wrong theme.
|
|
||||||
(function () {
|
|
||||||
const stored = localStorage.getItem('theme');
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
const dark = stored ? stored === 'dark' : prefersDark;
|
|
||||||
document.documentElement.classList.toggle('dark', dark);
|
|
||||||
})();
|
|
||||||
</script><link rel="stylesheet" href="/_astro/BaseLayout.z2Ysa6px.css"><script>window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };
|
|
||||||
var script = document.createElement('script');
|
|
||||||
script.defer = true;
|
|
||||||
script.src = '/_vercel/insights/script.js';
|
|
||||||
var head = document.querySelector('head');
|
|
||||||
head.appendChild(script);
|
|
||||||
</script></head><body class="flex min-h-screen flex-col bg-bg font-sans text-text antialiased selection:bg-primary selection:text-white"><header class="sticky top-0 z-40 border-b border-border/80 bg-bg/85 backdrop-blur"><div class="flex flex-col justify-center gap-4 px-4 py-3 sm:px-6"><div class="flex flex-row justify-between items-end"><a href="/pt" class="flex hover:underline shrink-0 items-center gap-2.5" aria-label="gabeefranco"><img src="/logo.png" class="h-4"><span class="font-mono text-[15px] font-semibold tracking-tight text-text">gabeefranco</span></a><div class="ml-10 flex sm:hidden justify-end gap-3"><a href="/tags/blogging/" hreflang="en" title="Mudar idioma" aria-label="Mudar idioma: English" class="text-accent max-w-fit font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep">Mudar idioma: English</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Alternar tema" title="Alternar tema">Alternar tema</button><script type="module">function e(){document.querySelectorAll(`.theme-toggle`).forEach(e=>{e.addEventListener(`click`,function(){let e=document.documentElement.classList.toggle(`dark`);localStorage.setItem(`theme`,e?`dark`:`light`)})})}e(),document.addEventListener(`astro:after-swap`,e);</script></div><div class="hidden sm:flex items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/pt" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Início</a><a href="/pt/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/pt/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Sobre</a></nav><a href="/tags/blogging/" hreflang="en" title="Mudar idioma" aria-label="Mudar idioma: English" class="text-accent max-w-fit py-1 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep ">Mudar idioma: English</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Alternar tema" title="Alternar tema">Alternar tema</button></div></div><div class="flex justify-end sm:hidden items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/pt" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Início</a><a href="/pt/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/pt/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Sobre</a></nav></div></div></header><main class="flex-1"><section class="mx-10 mt-10"><p class="text-sm font-mono font-medium text-text-muted">Posts com a tag</p><h1 class="mt-1 font-mono text-3xl font-bold tracking-tight text-text">#blogging</h1><a href="/pt/posts" class="font-mono mt-2 inline-block text-sm font-medium text-primary hover:underline">Limpar filtro</a><div class="mt-8"><article class="group border-b border-border py-6 first:pt-0 last:border-b-0 font-mono"><a href="/pt/posts/hello-world" class="block"><time datetime="2026-09-22T00:00:00.000Z" class="text-xs font-medium uppercase tracking-wide text-text-muted">21 de setembro de 2026</time><h3 class="mt-1.5 text-xl font-semibold text-text group-hover:text-primary group-hover:underline">O Hello World desse blog (ou seja lá como você queira chamar esse site)</h3><p class="mt-1.5 text-[15px] leading-relaxed text-text-muted">Uma nota curta sobre por que isso existe e sobre o que pretendo escrever aqui.</p></a><div class="mt-3 flex flex-wrap gap-2"><a href="/pt/tags/meta" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#meta</a><a href="/pt/tags/blogging" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#blogging</a></div></article></div></section></main><footer class="border-t border-border/80 font-mono"><div class="mx-auto flex max-w-3xl flex-col gap-2 px-4 py-8 text-sm text-text-muted sm:flex-row sm:items-center sm:justify-between sm:px-6"><p>© 2026 <span class="font-mono text-text">gabeefranco</span> · Todos os direitos reservados.</p><div class="flex items-center gap-4"><span class="hidden sm:inline">Escrito à mão em Porto Alegre.</span><a href="https://github.com/gabeefranco" target="_blank" rel="noopener noreferrer me" class="transition-colors hover:text-primary">GitHub</a><a href="https://github.com/gabeefranco/gabeefran.co-blog" target="_blank" rel="noopener noreferrer" class="transition-colors hover:text-primary">Código-fonte</a></div></div></footer></body></html>
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
<!DOCTYPE html><html lang="pt"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="alternate icon" href="/favicon.ico"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="canonical" href="https://gabeefran.co/pt/tags/life/"><link rel="alternate" hreflang="en" href="https://gabeefran.co/tags/life/"><link rel="alternate" hreflang="pt-BR" href="https://gabeefran.co/pt/tags/life/"><meta name="generator" content="Astro v7.2.10"><title>#life — gabeefranco</title><meta name="description" content="Tudo que já escrevi com a tag #life."><meta property="og:type" content="website"><meta property="og:title" content="#life — gabeefranco"><meta property="og:description" content="Tudo que já escrevi com a tag #life."><meta property="og:url" content="https://gabeefran.co/pt/tags/life/"><meta property="og:image" content="https://gabeefran.co/logo.png"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="#life — gabeefranco"><meta name="twitter:description" content="Tudo que já escrevi com a tag #life."><meta name="twitter:image" content="https://gabeefran.co/logo.png"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400..700;1,400..700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"><script>
|
|
||||||
// Runs before paint to avoid a flash of the wrong theme.
|
|
||||||
(function () {
|
|
||||||
const stored = localStorage.getItem('theme');
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
const dark = stored ? stored === 'dark' : prefersDark;
|
|
||||||
document.documentElement.classList.toggle('dark', dark);
|
|
||||||
})();
|
|
||||||
</script><link rel="stylesheet" href="/_astro/BaseLayout.z2Ysa6px.css"><script>window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };
|
|
||||||
var script = document.createElement('script');
|
|
||||||
script.defer = true;
|
|
||||||
script.src = '/_vercel/insights/script.js';
|
|
||||||
var head = document.querySelector('head');
|
|
||||||
head.appendChild(script);
|
|
||||||
</script></head><body class="flex min-h-screen flex-col bg-bg font-sans text-text antialiased selection:bg-primary selection:text-white"><header class="sticky top-0 z-40 border-b border-border/80 bg-bg/85 backdrop-blur"><div class="flex flex-col justify-center gap-4 px-4 py-3 sm:px-6"><div class="flex flex-row justify-between items-end"><a href="/pt" class="flex hover:underline shrink-0 items-center gap-2.5" aria-label="gabeefranco"><img src="/logo.png" class="h-4"><span class="font-mono text-[15px] font-semibold tracking-tight text-text">gabeefranco</span></a><div class="ml-10 flex sm:hidden justify-end gap-3"><a href="/tags/life/" hreflang="en" title="Mudar idioma" aria-label="Mudar idioma: English" class="text-accent max-w-fit font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep">Mudar idioma: English</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Alternar tema" title="Alternar tema">Alternar tema</button><script type="module">function e(){document.querySelectorAll(`.theme-toggle`).forEach(e=>{e.addEventListener(`click`,function(){let e=document.documentElement.classList.toggle(`dark`);localStorage.setItem(`theme`,e?`dark`:`light`)})})}e(),document.addEventListener(`astro:after-swap`,e);</script></div><div class="hidden sm:flex items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/pt" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Início</a><a href="/pt/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/pt/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Sobre</a></nav><a href="/tags/life/" hreflang="en" title="Mudar idioma" aria-label="Mudar idioma: English" class="text-accent max-w-fit py-1 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep ">Mudar idioma: English</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Alternar tema" title="Alternar tema">Alternar tema</button></div></div><div class="flex justify-end sm:hidden items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/pt" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Início</a><a href="/pt/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/pt/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Sobre</a></nav></div></div></header><main class="flex-1"><section class="mx-10 mt-10"><p class="text-sm font-mono font-medium text-text-muted">Posts com a tag</p><h1 class="mt-1 font-mono text-3xl font-bold tracking-tight text-text">#life</h1><a href="/pt/posts" class="font-mono mt-2 inline-block text-sm font-medium text-primary hover:underline">Limpar filtro</a><div class="mt-8"><article class="group border-b border-border py-6 first:pt-0 last:border-b-0 font-mono"><a href="/pt/posts/spotify-complaining" class="block"><time datetime="2026-09-02T03:00:00.000Z" class="text-xs font-medium uppercase tracking-wide text-text-muted">2 de setembro de 2026</time><h3 class="mt-1.5 text-xl font-semibold text-text group-hover:text-primary group-hover:underline">A mudança na cápsula do tempo do Spotify e a bostificação do software</h3><p class="mt-1.5 text-[15px] leading-relaxed text-text-muted">Spotify simplesmente removeu uma das suas melhores funcionalidades. Além de reclamar um pouco, quero abrir um debate sobre o que os softwares estão se tornando.</p></a><div class="mt-3 flex flex-wrap gap-2"><a href="/pt/tags/software" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#software</a><a href="/pt/tags/life" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#life</a></div></article></div></section></main><footer class="border-t border-border/80 font-mono"><div class="mx-auto flex max-w-3xl flex-col gap-2 px-4 py-8 text-sm text-text-muted sm:flex-row sm:items-center sm:justify-between sm:px-6"><p>© 2026 <span class="font-mono text-text">gabeefranco</span> · Todos os direitos reservados.</p><div class="flex items-center gap-4"><span class="hidden sm:inline">Escrito à mão em Porto Alegre.</span><a href="https://github.com/gabeefranco" target="_blank" rel="noopener noreferrer me" class="transition-colors hover:text-primary">GitHub</a><a href="https://github.com/gabeefranco/gabeefran.co-blog" target="_blank" rel="noopener noreferrer" class="transition-colors hover:text-primary">Código-fonte</a></div></div></footer></body></html>
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
<!DOCTYPE html><html lang="pt"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="alternate icon" href="/favicon.ico"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="canonical" href="https://gabeefran.co/pt/tags/meta/"><link rel="alternate" hreflang="en" href="https://gabeefran.co/tags/meta/"><link rel="alternate" hreflang="pt-BR" href="https://gabeefran.co/pt/tags/meta/"><meta name="generator" content="Astro v7.2.10"><title>#meta — gabeefranco</title><meta name="description" content="Tudo que já escrevi com a tag #meta."><meta property="og:type" content="website"><meta property="og:title" content="#meta — gabeefranco"><meta property="og:description" content="Tudo que já escrevi com a tag #meta."><meta property="og:url" content="https://gabeefran.co/pt/tags/meta/"><meta property="og:image" content="https://gabeefran.co/logo.png"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="#meta — gabeefranco"><meta name="twitter:description" content="Tudo que já escrevi com a tag #meta."><meta name="twitter:image" content="https://gabeefran.co/logo.png"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400..700;1,400..700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"><script>
|
|
||||||
// Runs before paint to avoid a flash of the wrong theme.
|
|
||||||
(function () {
|
|
||||||
const stored = localStorage.getItem('theme');
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
const dark = stored ? stored === 'dark' : prefersDark;
|
|
||||||
document.documentElement.classList.toggle('dark', dark);
|
|
||||||
})();
|
|
||||||
</script><link rel="stylesheet" href="/_astro/BaseLayout.z2Ysa6px.css"><script>window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };
|
|
||||||
var script = document.createElement('script');
|
|
||||||
script.defer = true;
|
|
||||||
script.src = '/_vercel/insights/script.js';
|
|
||||||
var head = document.querySelector('head');
|
|
||||||
head.appendChild(script);
|
|
||||||
</script></head><body class="flex min-h-screen flex-col bg-bg font-sans text-text antialiased selection:bg-primary selection:text-white"><header class="sticky top-0 z-40 border-b border-border/80 bg-bg/85 backdrop-blur"><div class="flex flex-col justify-center gap-4 px-4 py-3 sm:px-6"><div class="flex flex-row justify-between items-end"><a href="/pt" class="flex hover:underline shrink-0 items-center gap-2.5" aria-label="gabeefranco"><img src="/logo.png" class="h-4"><span class="font-mono text-[15px] font-semibold tracking-tight text-text">gabeefranco</span></a><div class="ml-10 flex sm:hidden justify-end gap-3"><a href="/tags/meta/" hreflang="en" title="Mudar idioma" aria-label="Mudar idioma: English" class="text-accent max-w-fit font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep">Mudar idioma: English</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Alternar tema" title="Alternar tema">Alternar tema</button><script type="module">function e(){document.querySelectorAll(`.theme-toggle`).forEach(e=>{e.addEventListener(`click`,function(){let e=document.documentElement.classList.toggle(`dark`);localStorage.setItem(`theme`,e?`dark`:`light`)})})}e(),document.addEventListener(`astro:after-swap`,e);</script></div><div class="hidden sm:flex items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/pt" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Início</a><a href="/pt/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/pt/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Sobre</a></nav><a href="/tags/meta/" hreflang="en" title="Mudar idioma" aria-label="Mudar idioma: English" class="text-accent max-w-fit py-1 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep ">Mudar idioma: English</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Alternar tema" title="Alternar tema">Alternar tema</button></div></div><div class="flex justify-end sm:hidden items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/pt" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Início</a><a href="/pt/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/pt/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Sobre</a></nav></div></div></header><main class="flex-1"><section class="mx-10 mt-10"><p class="text-sm font-mono font-medium text-text-muted">Posts com a tag</p><h1 class="mt-1 font-mono text-3xl font-bold tracking-tight text-text">#meta</h1><a href="/pt/posts" class="font-mono mt-2 inline-block text-sm font-medium text-primary hover:underline">Limpar filtro</a><div class="mt-8"><article class="group border-b border-border py-6 first:pt-0 last:border-b-0 font-mono"><a href="/pt/posts/hello-world" class="block"><time datetime="2026-09-22T00:00:00.000Z" class="text-xs font-medium uppercase tracking-wide text-text-muted">21 de setembro de 2026</time><h3 class="mt-1.5 text-xl font-semibold text-text group-hover:text-primary group-hover:underline">O Hello World desse blog (ou seja lá como você queira chamar esse site)</h3><p class="mt-1.5 text-[15px] leading-relaxed text-text-muted">Uma nota curta sobre por que isso existe e sobre o que pretendo escrever aqui.</p></a><div class="mt-3 flex flex-wrap gap-2"><a href="/pt/tags/meta" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#meta</a><a href="/pt/tags/blogging" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#blogging</a></div></article></div></section></main><footer class="border-t border-border/80 font-mono"><div class="mx-auto flex max-w-3xl flex-col gap-2 px-4 py-8 text-sm text-text-muted sm:flex-row sm:items-center sm:justify-between sm:px-6"><p>© 2026 <span class="font-mono text-text">gabeefranco</span> · Todos os direitos reservados.</p><div class="flex items-center gap-4"><span class="hidden sm:inline">Escrito à mão em Porto Alegre.</span><a href="https://github.com/gabeefranco" target="_blank" rel="noopener noreferrer me" class="transition-colors hover:text-primary">GitHub</a><a href="https://github.com/gabeefranco/gabeefran.co-blog" target="_blank" rel="noopener noreferrer" class="transition-colors hover:text-primary">Código-fonte</a></div></div></footer></body></html>
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
<!DOCTYPE html><html lang="pt"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="alternate icon" href="/favicon.ico"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="canonical" href="https://gabeefran.co/pt/tags/software/"><link rel="alternate" hreflang="en" href="https://gabeefran.co/tags/software/"><link rel="alternate" hreflang="pt-BR" href="https://gabeefran.co/pt/tags/software/"><meta name="generator" content="Astro v7.2.10"><title>#software — gabeefranco</title><meta name="description" content="Tudo que já escrevi com a tag #software."><meta property="og:type" content="website"><meta property="og:title" content="#software — gabeefranco"><meta property="og:description" content="Tudo que já escrevi com a tag #software."><meta property="og:url" content="https://gabeefran.co/pt/tags/software/"><meta property="og:image" content="https://gabeefran.co/logo.png"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="#software — gabeefranco"><meta name="twitter:description" content="Tudo que já escrevi com a tag #software."><meta name="twitter:image" content="https://gabeefran.co/logo.png"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400..700;1,400..700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"><script>
|
|
||||||
// Runs before paint to avoid a flash of the wrong theme.
|
|
||||||
(function () {
|
|
||||||
const stored = localStorage.getItem('theme');
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
const dark = stored ? stored === 'dark' : prefersDark;
|
|
||||||
document.documentElement.classList.toggle('dark', dark);
|
|
||||||
})();
|
|
||||||
</script><link rel="stylesheet" href="/_astro/BaseLayout.z2Ysa6px.css"><script>window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };
|
|
||||||
var script = document.createElement('script');
|
|
||||||
script.defer = true;
|
|
||||||
script.src = '/_vercel/insights/script.js';
|
|
||||||
var head = document.querySelector('head');
|
|
||||||
head.appendChild(script);
|
|
||||||
</script></head><body class="flex min-h-screen flex-col bg-bg font-sans text-text antialiased selection:bg-primary selection:text-white"><header class="sticky top-0 z-40 border-b border-border/80 bg-bg/85 backdrop-blur"><div class="flex flex-col justify-center gap-4 px-4 py-3 sm:px-6"><div class="flex flex-row justify-between items-end"><a href="/pt" class="flex hover:underline shrink-0 items-center gap-2.5" aria-label="gabeefranco"><img src="/logo.png" class="h-4"><span class="font-mono text-[15px] font-semibold tracking-tight text-text">gabeefranco</span></a><div class="ml-10 flex sm:hidden justify-end gap-3"><a href="/tags/software/" hreflang="en" title="Mudar idioma" aria-label="Mudar idioma: English" class="text-accent max-w-fit font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep">Mudar idioma: English</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Alternar tema" title="Alternar tema">Alternar tema</button><script type="module">function e(){document.querySelectorAll(`.theme-toggle`).forEach(e=>{e.addEventListener(`click`,function(){let e=document.documentElement.classList.toggle(`dark`);localStorage.setItem(`theme`,e?`dark`:`light`)})})}e(),document.addEventListener(`astro:after-swap`,e);</script></div><div class="hidden sm:flex items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/pt" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Início</a><a href="/pt/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/pt/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Sobre</a></nav><a href="/tags/software/" hreflang="en" title="Mudar idioma" aria-label="Mudar idioma: English" class="text-accent max-w-fit py-1 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep ">Mudar idioma: English</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Alternar tema" title="Alternar tema">Alternar tema</button></div></div><div class="flex justify-end sm:hidden items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/pt" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Início</a><a href="/pt/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/pt/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Sobre</a></nav></div></div></header><main class="flex-1"><section class="mx-10 mt-10"><p class="text-sm font-mono font-medium text-text-muted">Posts com a tag</p><h1 class="mt-1 font-mono text-3xl font-bold tracking-tight text-text">#software</h1><a href="/pt/posts" class="font-mono mt-2 inline-block text-sm font-medium text-primary hover:underline">Limpar filtro</a><div class="mt-8"><article class="group border-b border-border py-6 first:pt-0 last:border-b-0 font-mono"><a href="/pt/posts/spotify-complaining" class="block"><time datetime="2026-09-02T03:00:00.000Z" class="text-xs font-medium uppercase tracking-wide text-text-muted">2 de setembro de 2026</time><h3 class="mt-1.5 text-xl font-semibold text-text group-hover:text-primary group-hover:underline">A mudança na cápsula do tempo do Spotify e a bostificação do software</h3><p class="mt-1.5 text-[15px] leading-relaxed text-text-muted">Spotify simplesmente removeu uma das suas melhores funcionalidades. Além de reclamar um pouco, quero abrir um debate sobre o que os softwares estão se tornando.</p></a><div class="mt-3 flex flex-wrap gap-2"><a href="/pt/tags/software" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#software</a><a href="/pt/tags/life" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#life</a></div></article></div></section></main><footer class="border-t border-border/80 font-mono"><div class="mx-auto flex max-w-3xl flex-col gap-2 px-4 py-8 text-sm text-text-muted sm:flex-row sm:items-center sm:justify-between sm:px-6"><p>© 2026 <span class="font-mono text-text">gabeefranco</span> · Todos os direitos reservados.</p><div class="flex items-center gap-4"><span class="hidden sm:inline">Escrito à mão em Porto Alegre.</span><a href="https://github.com/gabeefranco" target="_blank" rel="noopener noreferrer me" class="transition-colors hover:text-primary">GitHub</a><a href="https://github.com/gabeefranco/gabeefran.co-blog" target="_blank" rel="noopener noreferrer" class="transition-colors hover:text-primary">Código-fonte</a></div></div></footer></body></html>
|
|
||||||
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
|
@ -1,15 +0,0 @@
|
||||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="alternate icon" href="/favicon.ico"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="canonical" href="https://gabeefran.co/tags/blogging/"><link rel="alternate" hreflang="en" href="https://gabeefran.co/tags/blogging/"><link rel="alternate" hreflang="pt-BR" href="https://gabeefran.co/pt/tags/blogging/"><meta name="generator" content="Astro v7.2.10"><title>#blogging — gabeefranco</title><meta name="description" content="Everything I’ve written tagged #blogging."><meta property="og:type" content="website"><meta property="og:title" content="#blogging — gabeefranco"><meta property="og:description" content="Everything I’ve written tagged #blogging."><meta property="og:url" content="https://gabeefran.co/tags/blogging/"><meta property="og:image" content="https://gabeefran.co/logo.png"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="#blogging — gabeefranco"><meta name="twitter:description" content="Everything I’ve written tagged #blogging."><meta name="twitter:image" content="https://gabeefran.co/logo.png"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400..700;1,400..700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"><script>
|
|
||||||
// Runs before paint to avoid a flash of the wrong theme.
|
|
||||||
(function () {
|
|
||||||
const stored = localStorage.getItem('theme');
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
const dark = stored ? stored === 'dark' : prefersDark;
|
|
||||||
document.documentElement.classList.toggle('dark', dark);
|
|
||||||
})();
|
|
||||||
</script><link rel="stylesheet" href="/_astro/BaseLayout.z2Ysa6px.css"><script>window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };
|
|
||||||
var script = document.createElement('script');
|
|
||||||
script.defer = true;
|
|
||||||
script.src = '/_vercel/insights/script.js';
|
|
||||||
var head = document.querySelector('head');
|
|
||||||
head.appendChild(script);
|
|
||||||
</script></head><body class="flex min-h-screen flex-col bg-bg font-sans text-text antialiased selection:bg-primary selection:text-white"><header class="sticky top-0 z-40 border-b border-border/80 bg-bg/85 backdrop-blur"><div class="flex flex-col justify-center gap-4 px-4 py-3 sm:px-6"><div class="flex flex-row justify-between items-end"><a href="/" class="flex hover:underline shrink-0 items-center gap-2.5" aria-label="gabeefranco"><img src="/logo.png" class="h-4"><span class="font-mono text-[15px] font-semibold tracking-tight text-text">gabeefranco</span></a><div class="ml-10 flex sm:hidden justify-end gap-3"><a href="/pt/tags/blogging/" hreflang="pt" title="Switch language" aria-label="Switch language: Português" class="text-accent max-w-fit font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep">Switch language: Português</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Toggle color theme" title="Toggle color theme">Toggle color theme</button><script type="module">function e(){document.querySelectorAll(`.theme-toggle`).forEach(e=>{e.addEventListener(`click`,function(){let e=document.documentElement.classList.toggle(`dark`);localStorage.setItem(`theme`,e?`dark`:`light`)})})}e(),document.addEventListener(`astro:after-swap`,e);</script></div><div class="hidden sm:flex items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Home</a><a href="/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">About</a></nav><a href="/pt/tags/blogging/" hreflang="pt" title="Switch language" aria-label="Switch language: Português" class="text-accent max-w-fit py-1 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep ">Switch language: Português</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Toggle color theme" title="Toggle color theme">Toggle color theme</button></div></div><div class="flex justify-end sm:hidden items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Home</a><a href="/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">About</a></nav></div></div></header><main class="flex-1"><section class="mx-10 mt-10"><p class="text-sm font-mono font-medium text-text-muted">Posts tagged</p><h1 class="mt-1 font-mono text-3xl font-bold tracking-tight text-text">#blogging</h1><a href="/posts" class="font-mono mt-2 inline-block text-sm font-medium text-primary hover:underline">Clear filter</a><div class="mt-8"><article class="group border-b border-border py-6 first:pt-0 last:border-b-0 font-mono"><a href="/posts/hello-world" class="block"><time datetime="2026-09-22T00:00:00.000Z" class="text-xs font-medium uppercase tracking-wide text-text-muted">September 21, 2026</time><h3 class="mt-1.5 text-xl font-semibold text-text group-hover:text-primary group-hover:underline">This blog's Hello World (or whatever you want to call this site)</h3><p class="mt-1.5 text-[15px] leading-relaxed text-text-muted">A short note on why this exists and what I plan to write about here.</p></a><div class="mt-3 flex flex-wrap gap-2"><a href="/tags/meta" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#meta</a><a href="/tags/blogging" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#blogging</a></div></article></div></section></main><footer class="border-t border-border/80 font-mono"><div class="mx-auto flex max-w-3xl flex-col gap-2 px-4 py-8 text-sm text-text-muted sm:flex-row sm:items-center sm:justify-between sm:px-6"><p>© 2026 <span class="font-mono text-text">gabeefranco</span> · All rights reserved.</p><div class="flex items-center gap-4"><span class="hidden sm:inline">Written by hand in Porto Alegre.</span><a href="https://github.com/gabeefranco" target="_blank" rel="noopener noreferrer me" class="transition-colors hover:text-primary">GitHub</a><a href="https://github.com/gabeefranco/gabeefran.co-blog" target="_blank" rel="noopener noreferrer" class="transition-colors hover:text-primary">Source</a></div></div></footer></body></html>
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="alternate icon" href="/favicon.ico"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="canonical" href="https://gabeefran.co/tags/life/"><link rel="alternate" hreflang="en" href="https://gabeefran.co/tags/life/"><link rel="alternate" hreflang="pt-BR" href="https://gabeefran.co/pt/tags/life/"><meta name="generator" content="Astro v7.2.10"><title>#life — gabeefranco</title><meta name="description" content="Everything I’ve written tagged #life."><meta property="og:type" content="website"><meta property="og:title" content="#life — gabeefranco"><meta property="og:description" content="Everything I’ve written tagged #life."><meta property="og:url" content="https://gabeefran.co/tags/life/"><meta property="og:image" content="https://gabeefran.co/logo.png"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="#life — gabeefranco"><meta name="twitter:description" content="Everything I’ve written tagged #life."><meta name="twitter:image" content="https://gabeefran.co/logo.png"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400..700;1,400..700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"><script>
|
|
||||||
// Runs before paint to avoid a flash of the wrong theme.
|
|
||||||
(function () {
|
|
||||||
const stored = localStorage.getItem('theme');
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
const dark = stored ? stored === 'dark' : prefersDark;
|
|
||||||
document.documentElement.classList.toggle('dark', dark);
|
|
||||||
})();
|
|
||||||
</script><link rel="stylesheet" href="/_astro/BaseLayout.z2Ysa6px.css"><script>window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };
|
|
||||||
var script = document.createElement('script');
|
|
||||||
script.defer = true;
|
|
||||||
script.src = '/_vercel/insights/script.js';
|
|
||||||
var head = document.querySelector('head');
|
|
||||||
head.appendChild(script);
|
|
||||||
</script></head><body class="flex min-h-screen flex-col bg-bg font-sans text-text antialiased selection:bg-primary selection:text-white"><header class="sticky top-0 z-40 border-b border-border/80 bg-bg/85 backdrop-blur"><div class="flex flex-col justify-center gap-4 px-4 py-3 sm:px-6"><div class="flex flex-row justify-between items-end"><a href="/" class="flex hover:underline shrink-0 items-center gap-2.5" aria-label="gabeefranco"><img src="/logo.png" class="h-4"><span class="font-mono text-[15px] font-semibold tracking-tight text-text">gabeefranco</span></a><div class="ml-10 flex sm:hidden justify-end gap-3"><a href="/pt/tags/life/" hreflang="pt" title="Switch language" aria-label="Switch language: Português" class="text-accent max-w-fit font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep">Switch language: Português</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Toggle color theme" title="Toggle color theme">Toggle color theme</button><script type="module">function e(){document.querySelectorAll(`.theme-toggle`).forEach(e=>{e.addEventListener(`click`,function(){let e=document.documentElement.classList.toggle(`dark`);localStorage.setItem(`theme`,e?`dark`:`light`)})})}e(),document.addEventListener(`astro:after-swap`,e);</script></div><div class="hidden sm:flex items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Home</a><a href="/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">About</a></nav><a href="/pt/tags/life/" hreflang="pt" title="Switch language" aria-label="Switch language: Português" class="text-accent max-w-fit py-1 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep ">Switch language: Português</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Toggle color theme" title="Toggle color theme">Toggle color theme</button></div></div><div class="flex justify-end sm:hidden items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Home</a><a href="/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">About</a></nav></div></div></header><main class="flex-1"><section class="mx-10 mt-10"><p class="text-sm font-mono font-medium text-text-muted">Posts tagged</p><h1 class="mt-1 font-mono text-3xl font-bold tracking-tight text-text">#life</h1><a href="/posts" class="font-mono mt-2 inline-block text-sm font-medium text-primary hover:underline">Clear filter</a><div class="mt-8"><article class="group border-b border-border py-6 first:pt-0 last:border-b-0 font-mono"><a href="/posts/spotify-complaining" class="block"><time datetime="2026-09-02T03:00:00.000Z" class="text-xs font-medium uppercase tracking-wide text-text-muted">September 2, 2026</time><h3 class="mt-1.5 text-xl font-semibold text-text group-hover:text-primary group-hover:underline">Spotify's Time Capsule Change and the Enshittification of Software</h3><p class="mt-1.5 text-[15px] leading-relaxed text-text-muted">Spotify just removed one of its best features. Beyond a bit of complaining, I want to open a debate about what software is turning into.</p></a><div class="mt-3 flex flex-wrap gap-2"><a href="/tags/software" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#software</a><a href="/tags/life" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#life</a></div></article></div></section></main><footer class="border-t border-border/80 font-mono"><div class="mx-auto flex max-w-3xl flex-col gap-2 px-4 py-8 text-sm text-text-muted sm:flex-row sm:items-center sm:justify-between sm:px-6"><p>© 2026 <span class="font-mono text-text">gabeefranco</span> · All rights reserved.</p><div class="flex items-center gap-4"><span class="hidden sm:inline">Written by hand in Porto Alegre.</span><a href="https://github.com/gabeefranco" target="_blank" rel="noopener noreferrer me" class="transition-colors hover:text-primary">GitHub</a><a href="https://github.com/gabeefranco/gabeefran.co-blog" target="_blank" rel="noopener noreferrer" class="transition-colors hover:text-primary">Source</a></div></div></footer></body></html>
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="alternate icon" href="/favicon.ico"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="canonical" href="https://gabeefran.co/tags/meta/"><link rel="alternate" hreflang="en" href="https://gabeefran.co/tags/meta/"><link rel="alternate" hreflang="pt-BR" href="https://gabeefran.co/pt/tags/meta/"><meta name="generator" content="Astro v7.2.10"><title>#meta — gabeefranco</title><meta name="description" content="Everything I’ve written tagged #meta."><meta property="og:type" content="website"><meta property="og:title" content="#meta — gabeefranco"><meta property="og:description" content="Everything I’ve written tagged #meta."><meta property="og:url" content="https://gabeefran.co/tags/meta/"><meta property="og:image" content="https://gabeefran.co/logo.png"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="#meta — gabeefranco"><meta name="twitter:description" content="Everything I’ve written tagged #meta."><meta name="twitter:image" content="https://gabeefran.co/logo.png"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400..700;1,400..700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"><script>
|
|
||||||
// Runs before paint to avoid a flash of the wrong theme.
|
|
||||||
(function () {
|
|
||||||
const stored = localStorage.getItem('theme');
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
const dark = stored ? stored === 'dark' : prefersDark;
|
|
||||||
document.documentElement.classList.toggle('dark', dark);
|
|
||||||
})();
|
|
||||||
</script><link rel="stylesheet" href="/_astro/BaseLayout.z2Ysa6px.css"><script>window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };
|
|
||||||
var script = document.createElement('script');
|
|
||||||
script.defer = true;
|
|
||||||
script.src = '/_vercel/insights/script.js';
|
|
||||||
var head = document.querySelector('head');
|
|
||||||
head.appendChild(script);
|
|
||||||
</script></head><body class="flex min-h-screen flex-col bg-bg font-sans text-text antialiased selection:bg-primary selection:text-white"><header class="sticky top-0 z-40 border-b border-border/80 bg-bg/85 backdrop-blur"><div class="flex flex-col justify-center gap-4 px-4 py-3 sm:px-6"><div class="flex flex-row justify-between items-end"><a href="/" class="flex hover:underline shrink-0 items-center gap-2.5" aria-label="gabeefranco"><img src="/logo.png" class="h-4"><span class="font-mono text-[15px] font-semibold tracking-tight text-text">gabeefranco</span></a><div class="ml-10 flex sm:hidden justify-end gap-3"><a href="/pt/tags/meta/" hreflang="pt" title="Switch language" aria-label="Switch language: Português" class="text-accent max-w-fit font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep">Switch language: Português</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Toggle color theme" title="Toggle color theme">Toggle color theme</button><script type="module">function e(){document.querySelectorAll(`.theme-toggle`).forEach(e=>{e.addEventListener(`click`,function(){let e=document.documentElement.classList.toggle(`dark`);localStorage.setItem(`theme`,e?`dark`:`light`)})})}e(),document.addEventListener(`astro:after-swap`,e);</script></div><div class="hidden sm:flex items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Home</a><a href="/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">About</a></nav><a href="/pt/tags/meta/" hreflang="pt" title="Switch language" aria-label="Switch language: Português" class="text-accent max-w-fit py-1 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep ">Switch language: Português</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Toggle color theme" title="Toggle color theme">Toggle color theme</button></div></div><div class="flex justify-end sm:hidden items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Home</a><a href="/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">About</a></nav></div></div></header><main class="flex-1"><section class="mx-10 mt-10"><p class="text-sm font-mono font-medium text-text-muted">Posts tagged</p><h1 class="mt-1 font-mono text-3xl font-bold tracking-tight text-text">#meta</h1><a href="/posts" class="font-mono mt-2 inline-block text-sm font-medium text-primary hover:underline">Clear filter</a><div class="mt-8"><article class="group border-b border-border py-6 first:pt-0 last:border-b-0 font-mono"><a href="/posts/hello-world" class="block"><time datetime="2026-09-22T00:00:00.000Z" class="text-xs font-medium uppercase tracking-wide text-text-muted">September 21, 2026</time><h3 class="mt-1.5 text-xl font-semibold text-text group-hover:text-primary group-hover:underline">This blog's Hello World (or whatever you want to call this site)</h3><p class="mt-1.5 text-[15px] leading-relaxed text-text-muted">A short note on why this exists and what I plan to write about here.</p></a><div class="mt-3 flex flex-wrap gap-2"><a href="/tags/meta" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#meta</a><a href="/tags/blogging" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#blogging</a></div></article></div></section></main><footer class="border-t border-border/80 font-mono"><div class="mx-auto flex max-w-3xl flex-col gap-2 px-4 py-8 text-sm text-text-muted sm:flex-row sm:items-center sm:justify-between sm:px-6"><p>© 2026 <span class="font-mono text-text">gabeefranco</span> · All rights reserved.</p><div class="flex items-center gap-4"><span class="hidden sm:inline">Written by hand in Porto Alegre.</span><a href="https://github.com/gabeefranco" target="_blank" rel="noopener noreferrer me" class="transition-colors hover:text-primary">GitHub</a><a href="https://github.com/gabeefranco/gabeefran.co-blog" target="_blank" rel="noopener noreferrer" class="transition-colors hover:text-primary">Source</a></div></div></footer></body></html>
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="alternate icon" href="/favicon.ico"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="canonical" href="https://gabeefran.co/tags/software/"><link rel="alternate" hreflang="en" href="https://gabeefran.co/tags/software/"><link rel="alternate" hreflang="pt-BR" href="https://gabeefran.co/pt/tags/software/"><meta name="generator" content="Astro v7.2.10"><title>#software — gabeefranco</title><meta name="description" content="Everything I’ve written tagged #software."><meta property="og:type" content="website"><meta property="og:title" content="#software — gabeefranco"><meta property="og:description" content="Everything I’ve written tagged #software."><meta property="og:url" content="https://gabeefran.co/tags/software/"><meta property="og:image" content="https://gabeefran.co/logo.png"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="#software — gabeefranco"><meta name="twitter:description" content="Everything I’ve written tagged #software."><meta name="twitter:image" content="https://gabeefran.co/logo.png"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400..700;1,400..700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"><script>
|
|
||||||
// Runs before paint to avoid a flash of the wrong theme.
|
|
||||||
(function () {
|
|
||||||
const stored = localStorage.getItem('theme');
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
const dark = stored ? stored === 'dark' : prefersDark;
|
|
||||||
document.documentElement.classList.toggle('dark', dark);
|
|
||||||
})();
|
|
||||||
</script><link rel="stylesheet" href="/_astro/BaseLayout.z2Ysa6px.css"><script>window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };
|
|
||||||
var script = document.createElement('script');
|
|
||||||
script.defer = true;
|
|
||||||
script.src = '/_vercel/insights/script.js';
|
|
||||||
var head = document.querySelector('head');
|
|
||||||
head.appendChild(script);
|
|
||||||
</script></head><body class="flex min-h-screen flex-col bg-bg font-sans text-text antialiased selection:bg-primary selection:text-white"><header class="sticky top-0 z-40 border-b border-border/80 bg-bg/85 backdrop-blur"><div class="flex flex-col justify-center gap-4 px-4 py-3 sm:px-6"><div class="flex flex-row justify-between items-end"><a href="/" class="flex hover:underline shrink-0 items-center gap-2.5" aria-label="gabeefranco"><img src="/logo.png" class="h-4"><span class="font-mono text-[15px] font-semibold tracking-tight text-text">gabeefranco</span></a><div class="ml-10 flex sm:hidden justify-end gap-3"><a href="/pt/tags/software/" hreflang="pt" title="Switch language" aria-label="Switch language: Português" class="text-accent max-w-fit font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep">Switch language: Português</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Toggle color theme" title="Toggle color theme">Toggle color theme</button><script type="module">function e(){document.querySelectorAll(`.theme-toggle`).forEach(e=>{e.addEventListener(`click`,function(){let e=document.documentElement.classList.toggle(`dark`);localStorage.setItem(`theme`,e?`dark`:`light`)})})}e(),document.addEventListener(`astro:after-swap`,e);</script></div><div class="hidden sm:flex items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Home</a><a href="/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">About</a></nav><a href="/pt/tags/software/" hreflang="pt" title="Switch language" aria-label="Switch language: Português" class="text-accent max-w-fit py-1 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep ">Switch language: Português</a><button type="button" class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep" aria-label="Toggle color theme" title="Toggle color theme">Toggle color theme</button></div></div><div class="flex justify-end sm:hidden items-center gap-2"><nav class="flex items-center gap-1 justify-right" aria-label="Menu"><a href="/" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Home</a><a href="/posts" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">Posts</a><a href="/about" class="px-2 py-1 text-sm font-medium font-mono underline text-text-muted hover:text-text">About</a></nav></div></div></header><main class="flex-1"><section class="mx-10 mt-10"><p class="text-sm font-mono font-medium text-text-muted">Posts tagged</p><h1 class="mt-1 font-mono text-3xl font-bold tracking-tight text-text">#software</h1><a href="/posts" class="font-mono mt-2 inline-block text-sm font-medium text-primary hover:underline">Clear filter</a><div class="mt-8"><article class="group border-b border-border py-6 first:pt-0 last:border-b-0 font-mono"><a href="/posts/spotify-complaining" class="block"><time datetime="2026-09-02T03:00:00.000Z" class="text-xs font-medium uppercase tracking-wide text-text-muted">September 2, 2026</time><h3 class="mt-1.5 text-xl font-semibold text-text group-hover:text-primary group-hover:underline">Spotify's Time Capsule Change and the Enshittification of Software</h3><p class="mt-1.5 text-[15px] leading-relaxed text-text-muted">Spotify just removed one of its best features. Beyond a bit of complaining, I want to open a debate about what software is turning into.</p></a><div class="mt-3 flex flex-wrap gap-2"><a href="/tags/software" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#software</a><a href="/tags/life" class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline">#life</a></div></article></div></section></main><footer class="border-t border-border/80 font-mono"><div class="mx-auto flex max-w-3xl flex-col gap-2 px-4 py-8 text-sm text-text-muted sm:flex-row sm:items-center sm:justify-between sm:px-6"><p>© 2026 <span class="font-mono text-text">gabeefranco</span> · All rights reserved.</p><div class="flex items-center gap-4"><span class="hidden sm:inline">Written by hand in Porto Alegre.</span><a href="https://github.com/gabeefranco" target="_blank" rel="noopener noreferrer me" class="transition-colors hover:text-primary">GitHub</a><a href="https://github.com/gabeefranco/gabeefran.co-blog" target="_blank" rel="noopener noreferrer" class="transition-colors hover:text-primary">Source</a></div></div></footer></body></html>
|
|
||||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |